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 :
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 :
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 à :
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▲
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▲
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▲
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
);
}
}