Archives mensuelles : avril 2008

Dessiner une partie d’image avec drawImage

Le besoin

3 cartes de tarot superposéesLa classe Graphics et sa fille Graphics2D offrent de nombreuses fonctions pour dessiner une image. Elles s'appellent toutes drawImage et prennent des arguments différents.

Je cherche aujourd’hui la bonne fonction pour dessiner une partie d'une image. J'en ai besoin dans le logiciel LeTarot, pour dessiner une carte à jouer qui est recouverte d'une autre carte, comme sur l'image ci-contre.

Pour fixer les idées, voici un exemple. J'ai une image de 50×50, et je veux afficher le morceau de cette image qui va de (0, 0) à (20, 20) à la position x=100, y=100 d'un JPanel. Quelle fonction faut-il utiliser ? Je vous laisse chercher un instant…

La mauvaise solution

Vous avez pensé à la fonction suivante ?

drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
          int sx1, int sy1, int sx2, int sy2, ImageObserver observer)

Vous avez raison, elle répond à la question. Voici comment l'utiliser pour l'exemple donné :

drawImage(carte, 100, 100, 120, 120, 0, 0, 20, 20, null)

Mais elle a un défaut : la performance. Cette fonction est capable de faire une mise à l'échelle. Or agrandir ou réduire une image prend du temps. De plus, cette image ne peut pas profiter de l'accélération de votre carte graphique. Dans notre cas, il n'y a aucun changement de taille, mais la fonction n'est peut-être pas assez maligne pour le détecter. Et de toute façon, l'accélération graphique est désactivée.

Use the clip, Luke, use the clip

Sans vous faire languir, voici la bonne fonction drawImage :

drawImage(Image img, int x, int y, ImageObserver observer)

Oui ! C'est la fonction de base, qui dessine l'image complète. Elle est aussi très rapide. Mais, me direz-vous, elle ne répond pas au besoin. En fait, il faut la combiner avec l'utilisation du clipping. Voici un extrait de code qui montre comment faire :

paintComponent(Graphics g) {
    Shape clip = g.getClip();
    g.clipRect(destX, destY, width, height);
    g.drawImage(img, destX, destY, null);
    g.setClip(clip);
}

Le clipping sert en général à ne dessiner que ce qui a changé depuis le dernier affichage. Mais il peut aussi servir à afficher efficacement une partie d'image.