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

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

III-B. Contruire le java.awt.PaintContext

 
Sélectionnez
  1. import java.awt.Color; 
  2. import java.awt.PaintContext; 
  3. import java.awt.Rectangle; 
  4. import java.awt.image.ColorModel; 
  5. import java.awt.image.Raster; 
  6. import sun.awt.image.IntegerComponentRaster; 
  7.  
  8. /** 
  9.  * Version : 1.0 
  10.  * @author JHelp 
  11.  * @version 1.0 
  12.  */ 
  13. public class PeintureRayeeVerticaleContexte implements PaintContext 
  14. { 
  15.    // Modèle de couleur utilisé 
  16.    private ColorModel modeleCouleur; 
  17.    // Rectangle englobant la forme à peindre 
  18.    private Rectangle rectangleEnglobant; 
  19.    // Couleurs de fond et de rayure 
  20.    private int couleurFond, couleurRayure; 
  21.    // Écart entre les rayures 
  22.    // Épaisseur des rayures 
  23.    // Place que prend le motif 
  24.    private int ecart, epaisseur, place; 
  25.  
  26.    /** 
  27.     * Construit le PaintContext pour peindre des rayures verticales 
  28.     * @param modeleCouleur Modèle de couleurs utilisé 
  29.     * @param rectangleEnglobant Rectangle englobant la forme 
  30.     * @param couleurFond Couleur du fond 
  31.     * @param couleurRayure Couleur des rayures 
  32.     * @param ecart Écart entre les rayures 
  33.     * @param epaisseur Épaisseur des rayures 
  34.     */ 
  35.    public PeintureRayeeVerticaleContexte(ColorModel modeleCouleur, 
  36.                                          Rectangle rectangleEnglobant, 
  37.                                          Color couleurFond, 
  38.                                          Color couleurRayure, 
  39.                                          int ecart, 
  40.                                          int epaisseur) 
  41.    { 
  42.       this.modeleCouleur = modeleCouleur; 
  43.       this.rectangleEnglobant = rectangleEnglobant; 
  44.       this.couleurFond = couleurFond.getRGB(); 
  45.       this.couleurRayure = couleurRayure.getRGB(); 
  46.       this.ecart = ecart; 
  47.       this.epaisseur = epaisseur; 
  48.       this.place = this.ecart + this.epaisseur; 
  49.    } 
  50.    /** 
  51.     * Détruit le contexte, ici rien n'est à détruire 
  52.     * @see java.awt.PaintContext#dispose() 
  53.     */ 
  54.    public void dispose() 
  55.    { 
  56.    } 
  57.  
  58.    /** 
  59.     * Modèle des couleurs 
  60.     * @return Modèle des couleurs 
  61.     * @see java.awt.PaintContext#getColorModel() 
  62.     */ 
  63.    public ColorModel getColorModel() 
  64.    { 
  65.       return this.modeleCouleur; 
  66.    } 
  67.  
  68.    /** 
  69.     * Construit le Raster et dessine dessus 
  70.     * @param x Abscisse du coin haut gauche du petit rectangle 
  71.     * @param y Ordonnée du coin haut gauche du petit rectangle 
  72.     * @param largeur Largeur du petit rectangle 
  73.     * @param hauteur Hauteur du petit rectangle 
  74.     * @return Le Raster peint 
  75.     * @see java.awt.PaintContext#getRaster(int, int, int, int) 
  76.     */ 
  77.    public Raster getRaster(int x, int y, int largeur, int hauteur) 
  78.    { 
  79.       // Construit le raster de la taille du petit rectangle 
  80.       IntegerComponentRaster raster = (IntegerComponentRaster)this.modeleCouleur. 
  81.           createCompatibleWritableRaster(largeur, hauteur); 
  82.       // Calcule l'abscisse du petit rectangle relativement à l'englobant 
  83.       int xx = x - this.rectangleEnglobant.x; 
  84.       // Offsett de démarrage de peinture (En général ça vaut 0) 
  85.       int off = raster.getDataOffset(0); 
  86.       // Largeur de peinture (En général c'est exactement la largeur du petit rectangle) 
  87.       int longueur = raster.getScanlineStride(); 
  88.       // Pixels du Raster à peindre 
  89.       int[] pixels = raster.getDataStorage(); 
  90.       // Pour chaque colonne du rectangle à faire 
  91.       for(int i=0; i<largeur; i++) 
  92.       { 
  93.          // Calcul de la couleur selon l'abscisse du point 
  94.          int couleur = this.couleurFond; 
  95.          if((i+xx)%this.place >= this.ecart) 
  96.          { 
  97.             couleur = this.couleurRayure; 
  98.          } 
  99.  
  100.          // Sur toute la colonne de l'abscisse en cours colorier de la même couleur 
  101.          for(int j=0; j<hauteur; j++) 
  102.          { 
  103.             // Le tableau de pixels est d'une seule dimension, bien que représentant une image à deux dimensions. 
  104.             // En fait la première ligne est sur les longueur premiers éléments, 
  105.             // la seconde sur les longueur suivants 
  106.             //... 
  107.             // off est le point de départ sur le tableau 
  108.             pixels[j*longueur+i+off] = couleur; 
  109.          } 
  110.       } 
  111.  
  112.       return raster; 
  113.    } 
  114. } 

III-C. Construire le java.awt.Paint

 
Sélectionnez
  1. import java.awt.Color; 
  2. import java.awt.Paint; 
  3. import java.awt.PaintContext; 
  4. import java.awt.Rectangle; 
  5. import java.awt.RenderingHints; 
  6. import java.awt.geom.AffineTransform; 
  7. import java.awt.geom.Rectangle2D; 
  8. import java.awt.image.ColorModel; 
  9.  
  10. /** 
  11.  * @author JHelp 
  12.  * @version 1.0 
  13.  */ 
  14. public class PeintureRayeeVerticale implements Paint 
  15. { 
  16.    // Couleur de fond et de rayures 
  17.    private Color couleurFond, couleurRayure; 
  18.    // Écart entre les rayures 
  19.    // Épaisseur des rayures 
  20.    private int ecart, epaisseur; 
  21.  
  22.    /** 
  23.     * Construit le Paint pour peindre des rayures verticales 
  24.     * @param couleurFond Couleur du fond 
  25.     * @param couleurRayure Couleur des rayures 
  26.     * @param ecart Écart entre les rayures 
  27.     * @param epaisseur Épaisseur des rayures 
  28.     */ 
  29.    public PeintureRayeeVerticale(Color couleurFond, Color couleurRayure, int ecart, int epaisseur) 
  30.    { 
  31.       if(couleurFond == null) 
  32.       { 
  33.          throw new NullPointerException("La couleur de fond ne doit pas être ); 
  34.       } 
  35.  
  36.       if(couleurRayure == null) 
  37.       { 
  38.          throw new NullPointerException("La couleur des rayures ne doit pas être ); 
  39.       } 
  40.  
  41.       if(ecart < 0) 
  42.       { 
  43.          throw new IllegalArgumentException("L'écart entre les rayures doit être positif ou nul"); 
  44.       } 
  45.  
  46.       if(epaisseur < 0) 
  47.       { 
  48.          throw new IllegalArgumentException("L'épaisseur des rayures doit être positive ou nulle"); 
  49.       } 
  50.  
  51.       this.couleurFond = couleurFond; 
  52.       this.couleurRayure = couleurRayure; 
  53.       this.ecart = ecart; 
  54.       this.epaisseur = epaisseur; 
  55.    } 
  56.  
  57.    /** 
  58.     * Crée le PaintContext, cette méthode est appelée par la fonction qui peint 
  59.     * @param cm Modèle de couleur 
  60.     * @param deviceBounds Rectangle englobant de la forme à peindre 
  61.     * @param userBounds Rectangle relatif de la forme à peindre ? (Pas sur) 
  62.     * @param xform Transformation que subit la peinture. Ici ne sert pas, mais 
  63.     *        on peut l'inclure en paramètre du PaintContext, ce qui par la suite 
  64.     *        permet de l'utiliser pour transformer la forme obtenue 
  65.     * @param hints ?Je ne sais pas encore? 
  66.     * @return Le PaintContext 
  67.     * @see java.awt.Paint#createContext(java.awt.image.ColorModel, java.awt.Rectangle, 
  68.     *      java.awt.geom.Rectangle2D, java.awt.geom.AffineTransform, java.awt.RenderingHints) 
  69.     */ 
  70.    public PaintContext createContext(ColorModel cm, 
  71.                                      Rectangle deviceBounds, 
  72.                                      Rectangle2D userBounds, 
  73.                                      AffineTransform xform, 
  74.                                      RenderingHints hints) 
  75.    { 
  76.       return new PeintureRayeeVerticaleContexte(cm, deviceBounds, this.couleurFond, 
  77.           this.couleurRayure, this.ecart, this.epaisseur); 
  78.    } 
  79.  
  80.    /** 
  81.     * Indique le mode de transparence, cela sert à la méthode qui peint pour optimiser quand c'est totalement opaque 
  82.     * @return Le mode de transparence 
  83.     * @see java.awt.Transparency#getTransparency() 
  84.     */ 
  85.    public int getTransparency() 
  86.    { 
  87.       // Si l'une des couleurs a un facteur de transparence non maximum, alors il 
  88.       // faut gérer la transparence, sinon c'est inutile, car c'est totalement opaque 
  89.       if(this.couleurFond.getAlpha()<255 || this.couleurRayure.getAlpha()<255) 
  90.       { 
  91.          return Paint.TRANSLUCENT; 
  92.       } 
  93.       return Paint.OPAQUE; 
  94.    } 
  95. } 

III-D. Exemple d'utilisation

 
Sélectionnez
  1. import java.awt.BorderLayout; 
  2. import java.awt.Color; 
  3. import java.awt.Dimension; 
  4. import java.awt.Graphics; 
  5. import java.awt.Graphics2D; 
  6. import java.awt.image.BufferedImage; 
  7. import javax.swing.JFrame; 
  8. import javax.swing.JLabel; 
  9. import javax.swing.UIManager; 
  10.  
  11. /** 
  12.  * @author JHelp 
  13.  * @version 1.0 
  14.  */ 
  15. public class Test extends JFrame 
  16. { 
  17.    /** 
  18.     * Dessin 
  19.     * @author JHelp 
  20.     * @version 1.0 
  21.     */ 
  22.    public class Dessin extends JLabel 
  23.    { 
  24.       // Image portant le dessin 
  25.       private BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_ARGB); 
  26.       /** 
  27.        * Construit le composant portant l'image 
  28.        */ 
  29.  
  30.       public Dessin() 
  31.       { 
  32.          this.setPreferredSize(new Dimension(200, 200)); 
  33.          // Dessine l'image 
  34.          Graphics2D g2 = this.image.createGraphics();; 
  35.          g2.setPaint(new PeintureRayeeVerticale(Color.BLACK, Color.CYAN, 10, 3)); 
  36.          g2.fillOval(20, 50, 100, 50); 
  37.          g2.setPaint(new PeintureRayeeVerticale(new Color(255, 255, 255, 127), Color.RED, 5, 1)); 
  38.          g2.fillOval(50, 20, 50, 100); 
  39.       } 
  40.  
  41.       /** 
  42.        * Dessine le composant 
  43.        * @param g Environement graphique 
  44.        * @see javax.swing.JComponent#paintComponent(java.awt.Graphics) 
  45.        */ 
  46.  
  47.       protected void paintComponent(Graphics g) 
  48.       { 
  49.          g.drawImage(this.image, (this.getWidth()-200)/2, (this.getHeight()-200)/2, this); 
  50.       } 
  51.    } 
  52.     
  53.    /** 
  54.     * Construit le test 
  55.     */ 
  56.    public Test() 
  57.    { 
  58.       super("Test - Paint"); 
  59.       this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
  60.       this.getContentPane().setLayout(new BorderLayout()); 
  61.       this.getContentPane().add(new Dessin(), BorderLayout.CENTER); 
  62.       this.pack(); 
  63.    } 
  64.  
  65.    /** 
  66.     * Lance le test 
  67.     */ 
  68.    public static void main(String[] args) 
  69.    { 
  70.       Test test = new Test(); 
  71.       test.setVisible(true); 
  72.    } 
  73. } 
Image non disponible
Capture de l'exemple