IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Tutoriel sur l'utilisation de Java2D pour définir une texture personnalisée

Cet article explique comment construire une texture personnalisée avec java.awt.Paint.

Pour réagir au contenu de cet article, un espace de dialogue vous est proposé sur le forum Commentez Donner une note  l'article (5).

Article lu   fois.

L'auteur

Profil Pro

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Qu'est-ce qu'un java.awt.Paint ?

Un java.awt.Paint est la description de la façon dont on va remplir une forme quelconque.
Il est applicable en Java2D via Graphics2D et sa méthode setPaint. Cette méthode définit ainsi la manière dont les méthodes fill… doivent remplir la surface. Il s'agit du motif de remplissage, mais pas du crayon qui permet ce remplissage, chose qui est gérée par setStroke.
Il existe trois java.awt.Paint tout prêts en standard dans Java : couleur unie, basé sur une image et dégradé à partir de deux couleurs.

Nous allons construire un Paint faisant des rayures verticales

II. Ce qu'il faut savoir pour comprendre

II-A. La représentation des couleurs

Comme le Paint manipule les couleurs pixel par pixel sous la forme d'entiers, il est nécessaire de comprendre comment les couleurs sont représentées.

Les couleurs sont faites des quatre composantes, l'alpha, le rouge, le vert et le bleu.
Ces quantités vont de 0 à 255.
Le rouge, le vert et le bleu sont les quantités de rouge de vert et de bleu. L'alpha est le degré de transparence 0 totalement transparent, 255 totalement opaque. La classe Color reprend ces paramètres.
Une couleur est en fait un int. On peut récupérer cet entier de la classe Color grâce à getRGB(). Cet entier est codé sur quatre octets, un octet par composante de couleur. Pour récupérer les différentes composantes, on fait :

 
Sélectionnez
1.
2.
3.
4.
alpha = (couleur >> 24) & 0xFF;
rouge = (couleur >> 16) & 0xFF;
vert = (couleur >> 8) & 0xFF;
bleu = couleur & 0xFF;

Pour rassembler les composantes en une couleur :

 
Sélectionnez
couleur = (alpha & 0xFF) << 24 | (rouge & 0xFF) << 16 | (vert & 0xFF) << 8 | (bleu & 0xFF);

Les images, les composants graphiques, peuvent être vus comme des tableaux de pixels, chaque pixel étant un entier représentant une couleur. Ce tableau est en une seule dimension, bien qu'il représente des données en deux dimensions. Si largeur est la largeur de la vue 2D alors, les largeur premiers pixels représentent la première ligne, les largeur suivants la seconde ligne… Ce qui fait que pour accéder au pixel (x, y), on fait : pixels[x+y*largeur] si « pixels » est le tableau de pixels.

II-B. La façon dont le java.awt.Paint est dessiné à l'écran

Pour remplir une forme d'un Paint donné, il faut calculer le rectangle minimal englobant cette forme. Avec ce rectangle est construit un PaintContext.
Ensuite le rectangle est découpé en petits rectangles, et chacun de ces petits rectangles est rempli grâce à la méthode getRaster. En fait, le Raster fourni est sauvegardé pour que le dessin puisse se faire un peu plus tard, mais on peut faire comme si on dessinait immédiatement, car on dessine sur le Raster, qui lui est recopié à l'écran.
Comme le rectangle englobant est fourni au départ on le sauvegardera pour le calcul du dessin des petits rectangles.

III. Les étapes à travers un exemple

III-A. Description de l'exemple

Nous allons construire un Paint pour faire des rayures verticales, en plus des informations obligatoires, comme le ColorModel et le rectangle englobant, le PaintContexte va recevoir les paramètres de rayures.
Paramètres de rayures : une couleur de fond, une couleur de rayure, l'écart entre les rayures et l'épaisseur des rayures.
L'algorithme de dessin va donc ressembler à :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
couleurFond <- couleur du fond
couleurRayure <- couleur des rayures
ecart <- écart entre les rayures
epaisseur <- épaisseur des rayures
place <- écart + épaisseur
largeur <- largeur du rectangle englobant
larg <- largeur du petit rectangle
haut <- hauteur du petit rectangle
xr <- gauche du rectangle englobant
xp <- gauche du petit rectangle
x <- xp - xr
raster <- créer raster de taille larg x haut
Pour(i=0; i<larg; i++)
{
   couleur <- couleurFond
   Si((i+x)%place >= écart)
   {
      couleur <- couleurRayure
   }
   Pour(j=0; j<haut; j++)
   {
      raster(i, j) <- couleur
   }
}

Nous allons maintenant pouvoir créer le PaintContext, puis le Paint.

III-B. Contruire le java.awt.PaintContext

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
import java.awt.Color;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import sun.awt.image.IntegerComponentRaster;

/**
 * Version : 1.0
 * @author JHelp
 * @version 1.0
 */
public class PeintureRayeeVerticaleContexte implements PaintContext
{
   // Modèle de couleur utilisé
   private ColorModel modeleCouleur;
   // Rectangle englobant la forme à peindre
   private Rectangle rectangleEnglobant;
   // Couleurs de fond et de rayure
   private int couleurFond, couleurRayure;
   // Écart entre les rayures
   // Épaisseur des rayures
   // Place que prend le motif
   private int ecart, epaisseur, place;

   /**
    * Construit le PaintContext pour peindre des rayures verticales
    * @param modeleCouleur Modèle de couleurs utilisé
    * @param rectangleEnglobant Rectangle englobant la forme
    * @param couleurFond Couleur du fond
    * @param couleurRayure Couleur des rayures
    * @param ecart Écart entre les rayures
    * @param epaisseur Épaisseur des rayures
    */
   public PeintureRayeeVerticaleContexte(ColorModel modeleCouleur,
                                         Rectangle rectangleEnglobant,
                                         Color couleurFond,
                                         Color couleurRayure,
                                         int ecart,
                                         int epaisseur)
   {
      this.modeleCouleur = modeleCouleur;
      this.rectangleEnglobant = rectangleEnglobant;
      this.couleurFond = couleurFond.getRGB();
      this.couleurRayure = couleurRayure.getRGB();
      this.ecart = ecart;
      this.epaisseur = epaisseur;
      this.place = this.ecart + this.epaisseur;
   }
   /**
    * Détruit le contexte, ici rien n'est à détruire
    * @see java.awt.PaintContext#dispose()
    */
   public void dispose()
   {
   }

   /**
    * Modèle des couleurs
    * @return Modèle des couleurs
    * @see java.awt.PaintContext#getColorModel()
    */
   public ColorModel getColorModel()
   {
      return this.modeleCouleur;
   }

   /**
    * Construit le Raster et dessine dessus
    * @param x Abscisse du coin haut gauche du petit rectangle
    * @param y Ordonnée du coin haut gauche du petit rectangle
    * @param largeur Largeur du petit rectangle
    * @param hauteur Hauteur du petit rectangle
    * @return Le Raster peint
    * @see java.awt.PaintContext#getRaster(int, int, int, int)
    */
   public Raster getRaster(int x, int y, int largeur, int hauteur)
   {
      // Construit le raster de la taille du petit rectangle
      IntegerComponentRaster raster = (IntegerComponentRaster)this.modeleCouleur.
          createCompatibleWritableRaster(largeur, hauteur);
      // Calcule l'abscisse du petit rectangle relativement à l'englobant
      int xx = x - this.rectangleEnglobant.x;
      // Offsett de démarrage de peinture (En général ça vaut 0)
      int off = raster.getDataOffset(0);
      // Largeur de peinture (En général c'est exactement la largeur du petit rectangle)
      int longueur = raster.getScanlineStride();
      // Pixels du Raster à peindre
      int[] pixels = raster.getDataStorage();
      // Pour chaque colonne du rectangle à faire
      for(int i=0; i<largeur; i++)
      {
         // Calcul de la couleur selon l'abscisse du point
         int couleur = this.couleurFond;
         if((i+xx)%this.place >= this.ecart)
         {
            couleur = this.couleurRayure;
         }

         // Sur toute la colonne de l'abscisse en cours colorier de la même couleur
         for(int j=0; j<hauteur; j++)
         {
            // Le tableau de pixels est d'une seule dimension, bien que représentant une image à deux dimensions.
            // En fait la première ligne est sur les longueur premiers éléments,
            // la seconde sur les longueur suivants
            //...
            // off est le point de départ sur le tableau
            pixels[j*longueur+i+off] = couleur;
         }
      }

      return raster;
   }
}

III-C. Construire le java.awt.Paint

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
import java.awt.Color;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;

/**
 * @author JHelp
 * @version 1.0
 */
public class PeintureRayeeVerticale implements Paint
{
   // Couleur de fond et de rayures
   private Color couleurFond, couleurRayure;
   // Écart entre les rayures
   // Épaisseur des rayures
   private int ecart, epaisseur;

   /**
    * Construit le Paint pour peindre des rayures verticales
    * @param couleurFond Couleur du fond
    * @param couleurRayure Couleur des rayures
    * @param ecart Écart entre les rayures
    * @param epaisseur Épaisseur des rayures
    */
   public PeintureRayeeVerticale(Color couleurFond, Color couleurRayure, int ecart, int epaisseur)
   {
      if(couleurFond == null)
      {
         throw new NullPointerException("La couleur de fond ne doit pas être );
      }

      if(couleurRayure == null)
      {
         throw new NullPointerException("La couleur des rayures ne doit pas être );
      }

      if(ecart < 0)
      {
         throw new IllegalArgumentException("L'écart entre les rayures doit être positif ou nul");
      }

      if(epaisseur < 0)
      {
         throw new IllegalArgumentException("L'épaisseur des rayures doit être positive ou nulle");
      }

      this.couleurFond = couleurFond;
      this.couleurRayure = couleurRayure;
      this.ecart = ecart;
      this.epaisseur = epaisseur;
   }

   /**
    * Crée le PaintContext, cette méthode est appelée par la fonction qui peint
    * @param cm Modèle de couleur
    * @param deviceBounds Rectangle englobant de la forme à peindre
    * @param userBounds Rectangle relatif de la forme à peindre ? (Pas sur)
    * @param xform Transformation que subit la peinture. Ici ne sert pas, mais
    *        on peut l'inclure en paramètre du PaintContext, ce qui par la suite
    *        permet de l'utiliser pour transformer la forme obtenue
    * @param hints ?Je ne sais pas encore?
    * @return Le PaintContext
    * @see java.awt.Paint#createContext(java.awt.image.ColorModel, java.awt.Rectangle,
    *      java.awt.geom.Rectangle2D, java.awt.geom.AffineTransform, java.awt.RenderingHints)
    */
   public PaintContext createContext(ColorModel cm,
                                     Rectangle deviceBounds,
                                     Rectangle2D userBounds,
                                     AffineTransform xform,
                                     RenderingHints hints)
   {
      return new PeintureRayeeVerticaleContexte(cm, deviceBounds, this.couleurFond,
          this.couleurRayure, this.ecart, this.epaisseur);
   }

   /**
    * Indique le mode de transparence, cela sert à la méthode qui peint pour optimiser quand c'est totalement opaque
    * @return Le mode de transparence
    * @see java.awt.Transparency#getTransparency()
    */
   public int getTransparency()
   {
      // Si l'une des couleurs a un facteur de transparence non maximum, alors il
      // faut gérer la transparence, sinon c'est inutile, car c'est totalement opaque
      if(this.couleurFond.getAlpha()<255 || this.couleurRayure.getAlpha()<255)
      {
         return Paint.TRANSLUCENT;
      }
      return Paint.OPAQUE;
   }
}

III-D. Exemple d'utilisation

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.UIManager;

/**
 * @author JHelp
 * @version 1.0
 */
public class Test extends JFrame
{
   /**
    * Dessin
    * @author JHelp
    * @version 1.0
    */
   public class Dessin extends JLabel
   {
      // Image portant le dessin
      private BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_ARGB);
      /**
       * Construit le composant portant l'image
       */

      public Dessin()
      {
         this.setPreferredSize(new Dimension(200, 200));
         // Dessine l'image
         Graphics2D g2 = this.image.createGraphics();;
         g2.setPaint(new PeintureRayeeVerticale(Color.BLACK, Color.CYAN, 10, 3));
         g2.fillOval(20, 50, 100, 50);
         g2.setPaint(new PeintureRayeeVerticale(new Color(255, 255, 255, 127), Color.RED, 5, 1));
         g2.fillOval(50, 20, 50, 100);
      }

      /**
       * Dessine le composant
       * @param g Environement graphique
       * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
       */

      protected void paintComponent(Graphics g)
      {
         g.drawImage(this.image, (this.getWidth()-200)/2, (this.getHeight()-200)/2, this);
      }
   }
   
   /**
    * Construit le test
    */
   public Test()
   {
      super("Test - Paint");
      this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      this.getContentPane().setLayout(new BorderLayout());
      this.getContentPane().add(new Dessin(), BorderLayout.CENTER);
      this.pack();
   }

   /**
    * Lance le test
    */
   public static void main(String[] args)
   {
      Test test = new Test();
      test.setVisible(true);
   }
}
Image non disponible
Capture de l'exemple

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 © 2004 JHelp. Aucune reproduction, même partielle, ne peut être faite de ce site ni 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.