---> Introduction
---> Le haut et le bas niveau
---> Les ressources graphiques
---> OpenGL avec Libgdx
---> Graphique 2D :
- Insérer
une image
- Afficher
du texte
- Dessiner
avec Libgdx
Introduction
Vous êtes arrivé à la 6eme étape de mon tutoriel
félicitation ! J’ai décidé d’entamer le module graphique de Libgdx avant de
présenter la deuxième partie de la manipulation des entrées.
Vous allez voir les fonctionnalités que possède Libgdx grâce à son
module Graphique ainsi que la manière avec laquelle cette bibliothèque arrive à gérer les différences entre les deux plateformes Android et Desktop. Let’s
GO !!
L’un des modules déjà vu précédemment est le module graphique. Le module graphique de Libgdx fournit des informations sur l’écran de l’appareil et la fenêtre de l’application qu’on pourra bien évidemment la manipuler ainsi que plein d’autres données graphiques grâce aux méthodes de la classe Gdx.
Le haut et le bas niveau
Ce qui est merveilleux avec ce module
c’est la capacité d’accéder aux fonctionnalités de bas niveau à chaque fois que
le haut niveau ne suffit pas.
Le module graphique cache la complexité de la programmation graphique avec l'API OpenGL ES. Ce module fournit des méthodes pratiques pour obtenir des instances de fonctions OpenGL ES indépendamment de la plateforme. Il est également livré avec un ensemble de méthodes utiles pour obtenir des informations sur l'écran, telles que la résolution, la densité, l'orientation et aussi, la mesure de performance.
Le module graphique ne se limite pas aux fonctionnalités mentionnées ci-dessus, il est beaucoup plus grand que ça. Il contient une collection de classes qui rendent le développement des graphismes extrêmement facile. Il facilite les graphismes en 2D et 3D et ceci grâce à aux fonctionnalités de haut et de bas niveau qu’il possède et aussi grâce à l’implémentation des classes les plus utilisées en programmation des jeux.
Voici quelques fonctionnalités propres
au module graphique de libgdx :
2D
- Le rendu Sprite
- Les polices Bitmap
- Les systèmes de
particules
- La manipulation
Bitmap
- Bibliothèque de
manipulation de Bitmap
3D
- Orthographique
et la caméra point de vue
- Key-cadre et
l'animation du squelette
- Chargeurs de
modèles 3D (OBJ, MD5)
Classe Commune et bas niveau
·
Textures
·
Texture atlas
·
Vertex arrays
·
Vertex buffer objects (VBO)
·
Frame buffer objects (FBO)
·
Shaders
·
La gestion du contexte OpenGL.
Les ressources graphiques
L'interface Graphique fournit
des méthodes pour créer une gamme de ressources graphiques soit dépendante de
la plateforme soit indépendante de la plateforme.
On va d'abord jeter un œil sur les ressources qui sont créés par le biais de l'interface graphique :
- Pixmap encapsule des données (image)
résidantes dans la mémoire. les Pixmaps peuvent être créés à partir d'un
fichier ou en spécifiant la largeur,
la hauteur et le format pixel. L’utilisation la plus fréquente
est la préparation d’une image pour la télécharger sur le GPU (processeur graphique) et cela en l’enveloppant dans une texture par
exemple.
Pixmap est compatible avec OpenGL ES 1.0, 1.1 et
2.0.
- Texture est essentiellement un Pixmap résidant
dans la mémoire. Les textures peuvent être créées à partir de Pixmaps,
en spécifiant la largeur, la hauteur et le format de pixel ou en spécifiant
un fichier.
Texture est compatible avec OpenGL ES 1.0, 1.1 et
2.0.
- Font (police) peut être chargée à partir
d'un fichier ou être spécifiée via un nom de police. Une police possède
bien évidemment une taille ainsi qu'un style, tels que normal, gras,
italique…
Font est compatible avec OpenGL ES 1.0, 1.1 et
2.0.
Maintenant voici d'autres
ressources graphiques qu’on peut instancier sans l'interface graphique:
- Mesh (maillage) est responsable de la tenue d'une série de
points, lignes ou triangles sous la forme de sommets. Résidants soit en VRAM sous forme de VBO(Vertex
Buffer Objects) ou dans la RAM sous
forme de tableaux de vertex. Tout ce qui est dessiné dans Libgdx
sera transformé en une mesh et transféré au processeur graphique.
Mesh est compatible avec OpenGL ES 1.0, 1.1 et 2.0.
- SpriteBatch est la meilleure méthode pour générer
d’une façon simple le rendu d’objet de 2D, tels que les sprites et les textes. Dans le cas
de sprites on précise simplement la texture qui détient l'image du sprite
ainsi qu’'un ensemble d'attributs tels que la position des sprites ou
l'orientation.et dans le cas du texte on doit simplement spécifier la
police à utiliser pour le rendu du texte.
Le SpriteBatch est une classe incroyablement rapide et efficace, et elle
est compatible avec OpenGL ES 1.0, 1.1 et 2.0.
- BitmapFont est un autre moyen d’écrire du texte. Au lieu de charger une police système ou une police TTF fourni à partir d'un fichier, BitmapFont travaille avec les polices de bitmap AngleCode. Vous pouvez créer ces polices avec l'éditeur de thème TWL ou un outil appelé Hiéron .
- ShaderProgram enveloppe shaders
d’OpenGL ES 2.0. C'est une classe de convenance qui
allège la gestion des shaders en OpenGL ES.
ShaderProgram est uniquement disponibles lorsqu’OpenGL
ES 2.0 est utilisé.
- FrameBuffer enveloppe les objets FrameBuffer d’OpenGL
ES 2. Il s'agit d'une classe d'aide
simple qui devrait couvrir la plupart des utilisations FBO (frame buffer object). Les ressources sont gérées avec
une perte de contenu dans le cas de perte du contexte.
FrameBuffer est uniquement disponibles lorsqu’OpenGL
ES 2.0 est utilisé.
L’OpenGL avec Libgdx
Une utilisation particulière de
ce module concerne un accès plus direct au contexte OpenGL pour un niveau
inférieur de manipulation de données graphiques
Le module graphique possède des
méthodes de lecture qui vous retourneront une implémentation de l'une des
interfaces suivantes :
- GL10 , offre tous les fonctions d’OpenGL ES 1.0
- GL11 , offre tous les fonctions OpenGL ES 1.1. Il est
effectivement dérivé de GL10, donc, vous pouvez également appeler des
fonctions OpenGL ES 1.0 via cette interface.
- GL20 , offre tous les fonctions OpenGL ES 2.0.
- GLCommon , regroupe toutes les fonctions que OpenGL ES 1.x et OpenGL ES 2.0
partagent entre eux.
* Voici un exemple ou on accède
au contexte OpenGL ES2 dans une application pour définir le Viewport ;
effacer les tampons de trame et la profondeur.
Gdx.gl20.glViewport(0,0,Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
Gdx.gl20.glClearColor(0,0,0,1);
Gdx.gl20.glClear( GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT );
Gdx.gl20.glClearColor(0,0,0,1);
Gdx.gl20.glClear( GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT );
Notez que
l'utilisation du GL20 exige d’instruire la demande
d'utiliser OpenGL ES2 lors du démarrage. Donc on doit la spécifier au niveau
des classe lanceur d’application.
Il faut aussi savoir que
OpenGL ES 1.x et 2.0 sont incompatible et peuvent être indisponible sur certain
dispositif, c’est pour cela que l’interface graphique nous fournit des méthodes
pour vérifier qu’OpenGL ES implémenté est disponible sur la plateforme sous
laquelle est exécutée notre application/jeu.
Graphique 2D
On va se contenter de quelques fonctionnalités graphiques en 2D
pour ce tutoriel :
- Insérer une image :
Pour l’insertion d’une image on aura besoin de ces classes : Texture, TextureRegion, Spritebatch,
Sprite.
Texture :
La classe
texture décode un fichier image et le charge en mémoire GPU. Le fichier image doit être placé dans
le dossier "assets", tel que c’est décrit dans un précédant tutoriel.
Remarque :
La dimension de l'image doit être une
puissance de deux (16x16, 64x256, etc.)
* Voici un exemple de code
source :
private Texture texture;
texture = new Texture(Gdx.files.internal("image.png"));
L’image qui doit se trouver dans le dossier assets
du projet est maintenant chargée en mémoire.
Il y a plusieurs méthodes qui nous permettent de
dessiner une texture
· draw(Texture
texture, float x, float y)
· draw(Texture
texture, float x, float y, int ecrX, int ecrY, int ecrLargeur, int ecrHauteur)
· draw(Texture
texture, float x, float y, float largeur, float hauteur, int ecrX, int ecrY,
int ecrLargeur, int ecrHauteur, boolean flipX, boolean flipY)
· draw(Texture
texture, float x, float y, float origineX, float origineY, float largeur, float
hauteur, float scaleX, float scaleY, float rotation, int ecrX, int ecrY, int
ecrLargeur, int ecrHauteur, boolean flipX, boolean flipY)
· draw(Texture texture,
float x, float y, float largeur, float hauteur, float u, float v, float u2,
float v2)
· draw(Texture
texture, float[] spriteVertices, int offset, int longueur)
SpriteBatch :
Une fois l’image chargée, pour l’insérer il faut faire
appel au SpriteBatch qui va la dessiner sur l’écran de l’appareil selon les
coordonnées paramétrées lors de l’insertion.
* Voici un exemple de code source
private SpriteBatch batch;
public void create () {
batch = new SpriteBatch();
}
public void render () {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// Pour effacer
l’écran
batch.begin();
// l’insertion de l’image
s’effectue ici
batch.end();
}
Il faut savoir que lorsque
la texture est dessinée sur l’écran, les parties translucides de cette texture
sont fusionnés avec les pixels qui déjà sur l'écran à cet endroit. Cette
fonction s’appelle le blending elle est activée par défaut. Mais lorsqu’on
désactive le blending toute la place réservée pour la texture est
remplacé par la texture.
* Voici les deux lignes de code qui désactive
puis active le blending
public void render() {
batch.begin();
batch.disableBlending();
// insertion s'effectue ici
batch.enableBlending();
batch.end();
}
TextureRegion
On pourrait s’arrêter sur les deux premières classes si
on se contente de charger une image et l’insérer, mais pour plus de
fonctionnalité on fait appel à TextureRegion. Cette classe définie un rectangle
à l’intérieure d’une texture ceci est utile pour dessiner une partie de la
texture sur l’écran de l’appareil.
* Voici un exemple de code source :
private SpriteBatch batch;
private TextureRegion region;
private Texture texture;
public void create () {
texture = new Texture(Gdx.files.internal("image.png"));
region = new TextureRegion(texture, 50, 30, 60, 40);
}
public void render () {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(region, 10, 10);
batch.end();
}
Ici on a dessiné une partie de la l’image comme suite :
A gauche c’est le contenu de ma texture qui enveloppe « image »
qui n’est qu’un fond en vert de la taille 128x256 (la puissance de 2 est
obligatoire), et à droite on spécifie dans la texture la région qui va être dessiné
selon le code source ci-dessus
* Il y a une autre façon d’utiliser TextureRegion, dans le cas où on veut manipuler des régions de même hauteur et de même largeur voici comment il faut faire :
private Texture texture;
private SpriteBatch batch;
private TextureRegion region[][];
@Override
public void create() {
batch =new SpriteBatch();
texture = new Texture(Gdx.files.internal("image.png"));
region = TextureRegion.split(texture, 32, 32);
}
@Override
public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(region[0][0], 0, 0);
batch.end();
}
Ici on a dessiné la région de l’image comme suite :
A gauche vous avez la texture enveloppant « image » qui
représente un ensemble de petites images de taille 32x32 chacune.
A droite on spécifie la région qui va être dessiné
selon le code source ci-dessus.
Il y a plusieurs méthodes qui permettent de dessiner une région d’une texture.
- draw(TextureRegion region, float x, float y) // celle qu’on a utilisé dans l’exemple
- draw(TextureRegion region, float x, float y, float width, float height)
- draw(TextureRegion region, float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY, float rotation)
Sprite
La classe Sprite décrit à la fois une région de
texture, sa géométrie ainsi que la couleur tout ça dans un seul et unique
objet.
* Voici un exemple de code source :
private SpriteBatch batch;
private Texture texture;
private Sprite sprite;
public void create () {
texture = new Texture(Gdx.files.internal("image.png"));
sprite = new Sprite(texture, 50, 30, 60, 40);
sprite.setPosition(10, 10);
sprite.setRotation(45);
}
public void render () {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
batch.begin();
sprite.draw(sprite, 10, 10);
batch.end();
}
Le résultat est semblable à celui de la TextureRegion
sauf qu’ici il y a en plus la rotation.
A droite vous avez la texture, la même que l’exemple de
TextureRegion
A droite on spécifie le sprite qui va être dessiné sur
l’écran de l’appareil en position de 10px en hauteur en largeur et avec une
rotation de 45 degrés
- Dessiner avec libgdx
Pixmap :
On a dit que Pixmap prépare une image pour être télécharger dans le
GPU puis envelopper dans une texture ou seulement ranger pour une utilisation
future.
Pixmaps peuvent être créés à partir
d'un tableau d'octets contenant les données d'image codées
en tant que JPEG , PNG ou BMP , un FileHandle , ou en
spécifiant ses dimensions et son format.
On va utiliser Pixmap pour dessiner des images.
* Voici un code source qui crée un 64x64 32-bit RGBA Pixmap, qui
dessine un cercle coloré en vert à l'intérieur, il l’enveloppe dans une
texture, puis le dispose de la mémoire.
private Texture enveloppe;
private Pixmap pixmap;
private SpriteBatch batch;
@Override
public void create() {
batch = new SpriteBatch();
pixmap = new Pixmap( 64, 64, Format.RGBA8888 );
pixmap.setColor( 0, 1, 0, 1 );
pixmap.fillCircle( 32, 32, 32 );
enveloppe = new Texture( pixmap );
pixmap.dispose();
}
@Override
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(enveloppe, 0, 0);
batch.end();
}
On pourra bien évidemment dessiner des lignes, des rectangles et
même dessiner pixel par pixel
BitmapFont
On déjà vu comment afficher du texte dans les précédents tutoriels,
c’est une opération très facile voici un exemple de code source :
private Texture texture;
private SpriteBatch batch;
private BitmapFont fontmessage ;
private String message;
@Override
public void create() {
fontmessage = new BitmapFont();
batch = new SpriteBatch();
message = "Bonjour tous le monde";
}
@Override
public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
batch.begin();
fontmessage.draw(batch, message, 10, 30);
batch.end();
}
Maintenant, et comme d’habitude
on va récapituler avec un code source d’une petite application qui permet de
dessiner sur un écran avec un crayon rouge et d’effacer en utilisant une gomme.
Vous aurez besoin de ces deux
fichiers qui sont deux images trouvées dans google et modifiées pour qu'ils aillent un fond transparent.
Le code source est décomposé en 3 parties comme nous l’avons déjà
expliqué dans un tutoriel précédant.
* La partie logique contiendra :
package my.works.TutorielBlog;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import
com.badlogic.gdx.InputProcessor;
import
com.badlogic.gdx.graphics.Color;
import
com.badlogic.gdx.graphics.GL10;
import
com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import
com.badlogic.gdx.graphics.Pixmap.Format;
import
com.badlogic.gdx.graphics.g2d.Sprite;
import
com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.Array;
public class Graphique implements ApplicationListener {
private Texture rectangleTexture;
private SpriteBatch batch;
private Pixmap rectanglePixmap;
private Sprite traitSprite;
private Array<Sprite> tabRectangelSprite ;
private Texture curseurTexture;
private float posCurseurX ;
private float posCurseurY ;
private int compteur = -1;
private boolean toucher = false ;
public static int largeur ;
public static int hauteur ;
public static int tailleTrait = 4 ;
@Override
public void create() {
batch = new SpriteBatch();
tabRectangelSprite = new Array<Sprite>();
// Préparer le petit rectangle
constituant le trait
rectanglePixmap =new Pixmap(32,32,Format.RGB888);
rectanglePixmap.setColor(Color.RED);
rectanglePixmap.drawRectangle(0, 0, tailleTrait, tailleTrait);
rectanglePixmap.fillRectangle(0, 0, tailleTrait, tailleTrait);
// Mettre le petit rectangle
dans une texture
rectangleTexture = new Texture(rectanglePixmap,Format.RGB888,false);
// Initialement
c'est le crayon qui est chargé tel que curseur
curseurTexture = new Texture(Gdx.files.internal("crayon.png"));
// on recupere la taille de l'écran qui
// varie selon l'appareil
largeur = Gdx.graphics.getWidth();
hauteur = Gdx.graphics.getHeight();
}
@Override
public void dispose() {
}
@Override
public void pause() {
}
// Dessiner le curseur qui
est soit gomme soit crayon
public void dessinerCureur(){
batch.begin();
batch.draw(curseurTexture, posCurseurX -32, hauteur -posCurseurY - 32);
batch.end();
}
// Dessiner le trait qui
est ensemble de rectangle
public void dessinerTrait(boolean toucher){
if(toucher)
{
batch.begin();
tabRectangelSprite.get(compteur).draw(batch);
for(int i = 0 ; i< tabRectangelSprite.size ; i++)
tabRectangelSprite.get(i).draw(batch);
batch.end();
}
}
@Override
public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT );
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.input.setInputProcessor(new InputProcessor() {
@Override
public boolean touchUp(int arg0, int arg1, int arg2, int arg3) {
return false;
}
@Override
public boolean touchMoved(int x, int y) {
// Récupérer la position du
curseur
posCurseurX=x;
posCurseurY=y;
return false;
}
@Override
public boolean touchDragged(int x, int y, int pointeur) {
// Récupérer la position du
curseur
posCurseurX=x;
posCurseurY=y;
// Commencer à dessiner sur
l'écran
toucher = true ;
traitSprite = new Sprite(rectangleTexture,0,0,tailleTrait,tailleTrait);
traitSprite.setPosition(x - tailleTrait/2,(hauteur) - y - tailleTrait/2);
tabRectangelSprite.add(traitSprite) ;
// L’indice compteur du
nombre de rectangle formant un trait
compteur++;
return false;
}
@Override
public boolean touchDown(int arg0, int arg1, int arg2, int arg3) {
return false;
}
@Override
public boolean scrolled(int arg0) {
return false;
}
@Override
public boolean keyUp(int arg0) {
return false;
}
@Override
public boolean keyTyped(char arg0) {
return false;
}
@Override
public boolean keyDown(int arg0) {
// Changement
de curseur en gomme
if(Gdx.input.isKeyPressed(Keys.G))
{
// Recharger l'image
du curseur
curseurTexture = new Texture(Gdx.files.internal("gomme.png"));
// Recharger la couleur
rectanglePixmap.setColor(Color.WHITE);// La même couleur que
l'écran
rectanglePixmap.fillRectangle(0, 0, tailleTrait, tailleTrait);
// Recharger la texture
rectangleTexture = new Texture(rectanglePixmap,Format.RGB888,false);
}
// Changement de curseur en
crayon
if(Gdx.input.isKeyPressed(Keys.C))
{
// Recharger l'image du
curseur
curseurTexture = new Texture(Gdx.files.internal("crayon.png"));
// Recharger la couleur
rectanglePixmap.setColor(Color.RED);// la couleur rouge
rectanglePixmap.fillRectangle(0, 0, tailleTrait, tailleTrait);
// Recharger la texture
rectangleTexture = new Texture(rectanglePixmap,Format.RGB888,false);
}
return false;
}
});
dessinerTrait(toucher);
dessinerCureur();
}
@Override
public void resize(int arg0, int arg1) {
}
@Override
public void resume() {
}
}
* La partie Desktop : elle contient le code lanceur de
l’application sous Desktop
package my.works.TutorielBlog;
import
com.badlogic.gdx.backends.jogl.JoglApplication;
public class Lanceur {
public static void main(String[] args) {
new JoglApplication (new Graphique(),"Graphique",32*8,
32*14, false);
}
}
* La partie Android : elle contient le code lanceur de
l’application sous Android
package my.works.LibgdxTutorielAndroid;
import my.works.TutorielBlog.Graphique;
import com.badlogic.gdx.backends.android.AndroidApplication;
import android.os.Bundle;
public class main extends AndroidApplication {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle
savedInstanceState) {
super.onCreate(savedInstanceState);
initialize(new Graphique(), false);
}
}
La partie logique se trouve dans le projet Desktop avec le lanceur
de l’application, contrairement au lanceur Android qui se trouve dans un projet
appart. Nous avons déjà parlé de ça en détail dans un tutoriel précédant.
* Voici le résultat sur desktop :
Si on appuie sur la touche G du clavier on aura une gomme à la
place du stylo et on pourra commencer à effacer.
* Maintenant le résultat sous
Android (sous l’émulateur) :
Si on appuie sur la touche G du clavier on aura une gomme à la
place du stylo et on pourra effacer ce qu’on a dessiné.
Conclusion
Le module graphique de Libgdx
est vaste, et encore on a vu que les fonctionnalités graphiques en 2D, mais ne
vous inquiétez pas si vous avez compris ce que nous avons dans ce tutoriel
sachez que vous avez fait un grand pas. Si vous avez des remarques ou des
questions n’hésitez pas à les poster en commentaire. Merci pour votre lecture.
Bonjour, je voulais savoir si avec jogl il y a un moyen simple d'ajouter des boutons et voire meme faire un petit formulaire?
RépondreSupprimerD'avance merci
Bonjour far38, oui évidemment ceci est possible et c'est justement le sujet de mon prochain tutoriel,
RépondreSupprimerTout le plaisir est pour moi!
"Remarque :
RépondreSupprimerLa dimension de l'image doit être une puissance de deux (16x16, 64x256, etc.)"
Avec OpenGL 2.0, il est possible d'utiliser des textures ayant des dimensions autre que des puissances de 2
Merci pour l'article !
Pour éviter que la dimension de l'image soit une puissance de 2 tu peux utiliser dans la méthode create() de la classe game :
SupprimerTexture.setEnforcePotImages(false);
jai un probleme qui me dis au logcat package com.me.mydxgame has no certificates at entry assets/data/arriereplan.png et la meme chose pour tout les images :/
RépondreSupprimerCe commentaire a été supprimé par l'auteur.
RépondreSupprimerHelp !
RépondreSupprimerL'app crash sur android 4.4.2...
Il "manque des acceurs" au niveau de :
@Override
public boolean touchUp(int arg0, int arg1, int arg2, int arg3) {
return false;
}
@Override
public boolean touchMoved(int x, int y) {
// Récupérer la position du curseur
posCurseurX=x;
posCurseurY=y;
return false;
}
Help...
Bonjour , je viens d'essayer l'app sur mon Galaxy S4 qui en version 4.3 et ça marche
SupprimerJe pense que ton problème est avec la version de libgdx sache que j'ai utilisé une version ancienne de la bib la 0.9.6 quelque méthode ont changer au niveau de InputProcessor
Ah, nickelll !
RépondreSupprimerMerci beaucoup !
Tu pourrais m'envoyer un lien de ton projet ?
Que je vois si j'ai pas merdé un truc avec la classpath ou autre ? ._.
Un dropbox ou mega ^^
(Ou skype ou mail comme tu veux ^^ )
Merci en tout cas ! :)
acutalise la page tu trouvera le projet en bas de la page
SupprimerBonne continuation !
Ce commentaire a été supprimé par l'auteur.
RépondreSupprimer