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

JHelp

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 .

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

- Auteur : JHelp

- Parution : 21 avril 2004

- Licence : 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 et 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.

Sommaire

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

- II. Ce qu'il faut savoir pour comprendre

- II-A. La représentation des couleurs

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

- III. Les étapes à travers un exemple

- III-A. Description de l'exemple

- III-B. Contruire le java.awt.PaintContext

- III-C. Construire le java.awt.Paint

- III-D. Exemple d'utilisation

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 :

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 :

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 à :

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

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;

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;

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. }

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. }

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. }

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. }

112. return raster;

113. }

114. }

III-C - Construire le java.awt.Paint

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;

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;

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. }

36. if(couleurRayure == null)

37. {

38. throw new NullPointerException("La couleur des rayures ne doit pas être );

39. }

41. if(ecart < 0)

42. {

43. throw new IllegalArgumentException("L'écart entre les rayures doit être positif ou nul");

44. }

46. if(epaisseur < 0)

47. {

48. throw new IllegalArgumentException("L'épaisseur des rayures doit être positive ou nulle");

49. }

51. this.couleurFond = couleurFond;

52. this.couleurRayure = couleurRayure;

53. this.ecart = ecart;

54. this.epaisseur = epaisseur;

55. }

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. }

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

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;

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. */

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. }

41. /**

42. * Dessine le composant

43. * @param g Environement graphique

44. * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)

45. */

47. protected void paintComponent(Graphics g)

48. {

49. g.drawImage(this.image, (this.getWidth()-200)/2, (this.getHeight()-200)/2, this);

50. }

51. }

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. }

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. }

Capture de l'exemple