Next: 31 Le son
Up: Java: Programmation graphique
Previous: 29 Couleurs et Fontes
Subsections
Java fournit des facilités pour manipuler des images au format
GIF et JPEG . On trouvera dans les packages
java.awt et java.awt.image tout un ensemble de
classes pour le faire. Une image est un objet de la classe
java.awt.Image. On crée généralement un objet de type Image
en invoquant la méthode getImage de la classe java.applet.Applet
ou java.awt.Toolkit
uneImage = uneApplet.getImage(URL);
uneImage = uneApplet.getImage(URL, String);
uneImage = unComposant.getToolkit().getImage(URL);
uneImage = unComposant.getToolkit().getImage(String);
uneImage = unComposant.getToolkit().getImage(URL);
uneImage = Toolkit.getDefaultToolkit().getImage(URL);
uneImage = ToolKit.getDefaultToolkit().getImage(String);
uneImage = Toolkit.getDefaultToolkit().getImage(URL);
|
La méthode getImage ne se préoccupe pas du chargement de
l'image; elle se termine dès que le fichier image est trouvé. Le
chargement de l'image ne se fait que lorsque celle-ci doit être
réellement nécessaire : par exemple, lors de l'invocation de
la méthode drawImage.
Ce chargement se fait de manière asynchrone.
Ceci permet de ne
pas attendre la fin du chargement de l'image pour continuer la suite du programme.
Cette approche est évidemment essentielle pour pouvoir télécharger
des images sur le réseau sans bloquer l'application. C'est, par
exemple, des browser WEB qui peuvent afficher les images au fur et
mesure de leur chargement tout en permettant de continuer à
exécuter d'autres tâches.
Dans certaines applications, il faut donc contrôler le
chargement effectif de l'image; ce que l'on fera en utilisant un
MediaTracker ou en implantant la méthode
imageUpdate de l'interface ImageObserver.
L'exemple qui suit affiche une image à l'écran. Par souci de
simplicité, l'implantation est plutôt sommaire. Nous verrons un
peu plus loin les diverses manières de contrôler le chargement
des images.
public class UneImage extends java.applet.Applet {
java.awt.Image image;
public void init() {
image = getImage(getCodeBase(), "tourai.gif");
prepareImage(image, this);
}
public void paint(java.awt.Graphics g) { g.drawImage(image, 0, 0, this); }
}
|
L'affichage d'un objet de type Image se fait en invoquant la
méthode drawImage sur le contexte graphique
drawImage(Image, int, int, ImageObserver)
|
La méthode drawImage se décline sous les formes
suivantes:
drawImage(Image img, int x, int y, Color bgc, ImageObserver o)
drawImage(Image img, int x, int y, ImageObserver o)
drawImage(Image img, int x, int y, int l, int h, Color bgc, ImageObserver o)
drawImage(Image img, int x, int y, int l, int h, ImageObserver o)
drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgc, ImageObserver o)
drawImage(Image img, int, int, int, int, int, int, int, int, ImageObserver o)
|
où
- img est l'image à afficher
- (x, y) la position du coin supérieur gauche
dans le composant
- l et h sont la largeur et la hauteur de la
portion de image
- bgc est la couleur du fond de l'image pour les zones
transparentes
- o est l'objet qui reçoit la notification du chargement
de l'image
- (dx1, dy1) et (dx2, dy2) sont les coins (supérieur gauche et
inférieur droit) de l'image destination
- (sx1, sy1) et (sx2, sy2) sont les coins (supérieur gauche et
inférieur droit) de l'image source
public class UneImageee extends java.applet.Applet {
java.awt.Image image;
public void init() {
image = getImage(getCodeBase(), "photo.jpg");
}
public void paint (java.awt.Graphics g) {
g.drawImage(image, 0, 0, 400, 150, this);
g.drawImage(image, 0, 0, 80, 80, this);
}
}
|
Comme nous l'avons dit, le chargement d'une image ne se fait que
lors qu'on en a réellement besoin :
Les objets qui manipulent les images se rangent dans trois catégories:
- Les producteurs d'images qui implantent l'interface
ImageProducer. Ceux-ci créent des pixels et le distribuent
à des consommateurs d'images.
- Les consommateurs d'images qui implantent l'interface
ImageConsumer. Ils récupèrent des pixels et les utilisent
pour les afficher ou les analyser.
- Les observateurs d'images qui implantent l'interface
ImageObserver. Les observateurs d'images prennent
connaissance de l'état de chargement du ou des images qu'ils
observent. Le dernier argument de la méthode drawImage est
un observateur d'image. Dans les exemples que nous venons de voir,
l'observateur d'image que nous avons fournit est un objet de type
Applet. En effet, la classe Component implante
l'interface ImageObserver et fournit une implantation de la
méthode ImageUpdate.
Lorsqu'on utilise des images (particulièrement avec les applets
pour lesquelles leur chargement est généralement long), il
convient de vérifier si l'image est effectivement disponible
pour affichage ou pas.
public void paint(Graphics g) {
int n = checkImage(im, this) & ImageObserver.ALLBITS;
if (n == ImageObserver.ALLBITS) g.drawImage(im, 50, 50, this);
else g.drawString("Chargement en cours ...")
}
|
Nous verrons dans la section suivante tous les champs de la classe
ImageObserver.
L'interface ImageObserver définit une seule méthode
imageUpdate qui est invoquée chaque fois qu'une portion
d'image est chargée. En implantant cette méthode, un composant peut
définir ce qu'il convient de faire à chaque acquisition d'une partie
de l'image (l'afficher ou le transformer etc.)
public interface ImageObserver {
public static final int WIDTH
public static final int HEIGHT
public static final int PROPERTIES
public static final int SOMEBITS
public static final int FRAMEBITS
public static final int ALLBITS
public static final int ERROR
public static final int ABORT
public abstract boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height)
}
|
public class Observ extends java.applet.Applet {
java.awt.TextArea textArea;
java.awt.Image image;
Trombine panel;
String rc = System.getProperty("line.separator");
public void init() {
textArea = new java.awt.TextArea(8, 50);
textArea.setEditable(false);
add(textArea);
image = getImage(getCodeBase(), "photo.jpg");
panel = new Trombine(image, this);
add(panel);
prepareImage(image, panel);
}
public void afficher(String s) { textArea.append(s + rc); textArea.setCaretPosition(2000); }
}
class Trombine extends java.awt.Panel {
java.awt.Dimension d = new java.awt.Dimension(150, 150);
java.awt.Image image;
Observ app;
boolean tout = false;
public Trombine(java.awt.Image image, Observ app) { this.image = image; this.app = app; }
public void paint(java.awt.Graphics g) { if (tout) g.drawImage(image, 0, 0, this); }
public java.awt.Dimension getMinimumSize() { return d; }
public java.awt.Dimension getPreferredSize() { return getMinimumSize(); }
public boolean imageUpdate(java.awt.Image img, int infoflags, int x, int y, int width, int height) {
String str = "x="+x+" ";
str += "y="+y+" ";
str += "width="+width+" ";
str += "height="+height+" ";
str += " infoflags=";
if ((infoflags & ABORT) != 0) str += "ABORT ";
if ((infoflags & ALLBITS) != 0)
{ str += "ALLBITS "; tout = true; repaint(); }
if ((infoflags & ERROR) != 0) str += "ERROR ";
if ((infoflags & FRAMEBITS) != 0) str += "FRAMEBITS ";
if ((infoflags & HEIGHT) != 0) str += "HEIGHT ";
if ((infoflags & PROPERTIES) != 0) str += "PROPERTIES ";
if ((infoflags & SOMEBITS) != 0) str += "SOMEBITS ";
if ((infoflags & WIDTH)!= 0) str += "WIDTH ";
app.afficher(str);
return true;
}
}
|
On a rarement besoin d'un contrôle si fin du chargement des images;
en général, on veut juste savoir si notre image est disponible ou pas.
Dans ces cas, on pourra utiliser la
classe MediaTracker qui fournit les méthodes pour charger des
images (chechID, checkAll) et attendre que les
images soit chargées (waitForID, waitAll).
public class MediaTracker implements Serializable {
public static final int LOADING
public static final int ABORTED
public static final int ERRORED
public static final int COMPLETE
public MediaTracker(Component comp)
public void addImage(Image image, int id)
public void addImage(Image image, int id, int w, int h)
public boolean checkAll()
public boolean checkAll(boolean load)
public boolean isErrorAny()
public Object[] getErrorsAny()
public void waitForAll() throws InterruptedException
public boolean waitForAll(long ms) throws InterruptedException
public int statusAll(boolean load)
public boolean checkID(int id)
public boolean checkID(int id, boolean load)
public boolean isErrorID(int id)
public Object[] getErrorsID(int id)
public void waitForID(int id) throws InterruptedException
public boolean waitForID(int id, long ms) throws InterruptedException
public int statusID(int id, boolean load)
public void removeImage(Image image)
public void removeImage(Image image, int id)
public void removeImage(Image image, int id, int width, int height)
}
|
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
public class MTracker extends Applet implements ActionListener {
Button b;
Fenetre fenetre;
Image[] images;
MediaTracker tracker;
public void init() {
b = new Button("Cliquez ici pour ouvrir/fermer une fenetre");
add(b);
b.addActionListener(this);
images = new Image[16];
tracker = new MediaTracker(this);
for (int i = 1; i <= 16; i++) {
images[i-1] = getImage(getCodeBase(), "tumble/t"+i+".gif");
tracker.addImage(images[i-1], 0);
}
fenetre = new Fenetre("MediaTracker", images, tracker);
}
public void actionPerformed(ActionEvent e) {
if (! fenetre.isShowing()) fenetre.setVisible(true);
else fenetre.setVisible(false);
}
public static void main(String[] args) {
Frame f = new Frame("Position et Dimensions");
MTracker p = new MTracker();
f.addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0); }
});
p.init();
f.add(p);
f.pack();
f.show();
}
}
class Fenetre extends Frame implements ActionListener {
MCanvas f;
Button b1, b2, b3, b4;
public Fenetre(String s, Image [] images, MediaTracker tracker) {
super(s);
setLayout(new BorderLayout());
add("Center", f = new MCanvas(images, tracker));
add("North", b1 = new Button("Premier"));
add("East", b2 = new Button("Dernier"));
add("South", b3 = new Button("Suivant"));
add("West", b4 = new Button("Precedant"));
b1.addActionListener(this);
b2.addActionListener(this);
b3.addActionListener(this);
b4.addActionListener(this);
setSize(250, 100);
}
public void actionPerformed(ActionEvent e) {
String s = e.getActionCommand();
if ("Premier".equals(s)) f.num = 0;
else if ("Dernier".equals(s)) f.num = 15;
else if ("Suivant".equals(s)) f.num = (f.num+1)%16 ;
else if ("Precedant".equals(s)) f.num = (f.num+15)%16;
f.repaint();
}
class MCanvas extends Canvas {
Image[] images;
MediaTracker tracker;
int num = 0;
Dimension d = new Dimension(130, 80);
public MCanvas(Image[] images, MediaTracker tracker) {
setBackground(Color.white);
this.images = images;
this.tracker = tracker;
try {
tracker.waitForAll(); // Attendre le chargement
}
catch (InterruptedException e) {
}
setSize(130, 80);
}
public void paint(Graphics g) {
if (! tracker.checkAll()) { g.drawString("Please wait...", 10, 10); }
else g.drawImage(images[num], 0, 0, this);
}
public Dimension getMinimumSize() { return d;}
public Dimension getPreferredSize() { return d;}
}
}
|
package td.threads;
import java.applet.*;
import java.awt.*;
public class Tumble extends Applet implements Runnable {
Image im[] = new Image[32];
Image imCourant;
Image Ivir;
Graphics Gvir;
Thread th;
public void init() {
Ivir = createImage(300,300);
Gvir = Ivir.getGraphics();
for (int i=0; i<16; i++) {
im[i] = getImage(getCodeBase(), "tumble/T"+(i+1)+".gif");
im[31-i] = getImage(getCodeBase(), "tumble/T"+(i+1)+".gif");
imCourant = im[0];
}
}
public void start() { if (th == null) { th = new Thread(this); th.start(); } }
public void stop() { if (th != null) { th.stop(); th = null; } }
public void run() {
while (true) {
for (int i=0; i<32; i++) {
try { th.sleep(100); }
catch (InterruptedException e) {}
imCourant = im[i];
repaint();
}
try { th.sleep(1000); }
catch (InterruptedException e) {}
}
}
public void paint(Graphics g) {
Gvir.drawImage(imCourant, 15, 50, this);
//g.drawImage(imCourant, 15, 50, this);
g.drawImage(Ivir, 0, 0, this);
}
}
|
A TERMINER
Un producteur d'images est un objet d'une classe qui implante
l'interface ImageProducer. Il produit des images pour un ou
plusieurs consommateurs. Dans beaucoup de cas, il n'est pas nécessaire
de soucier des producteurs et consommateurs d'images. La production et
la consommation d'images se fait de manière transparente pour le
programmeur.
Décrivons un peu la face caché d'une utilisation standard d'une
image. De manière simplifiée, un code comme celui suit devrait
exister dans une telle application:
public class UneApplet extends Applet {
private Image img = null;
public void init() {
img = getImage(getDocumentBase(), "image.gif");
prepareImage(img, this);
}
public void paint(Graphics g) { g.drawImage(img, 50, 50, this); }
}
|
La création d'une image nécessite un producteur d'images. Ici, ce
producteur est caché profondément dans l'implantation de la classe
Component.
La préparation de l'image et son affichage requiert un
consommateur d'images qui, lui aussi, est caché profondément
dans l'implantation de la classe
Component.
Enfin, au fur et à mesure du chargement de l'image, l'observateur
d'images reçoit une notification de l'état du chargement. Cet
observateur est également caché l'implantation de la classe
Component.
Entre la production d'images et la consommation d'images, il est
possible de placer un filtre de transformation. Un filtre de
transformation est un objet la classe
ImageFilter ou de l'une de ses classes dérivées:
- ReplicateScaleFilter : qui permet de
redimensionner une image selon un algorithme relativement simpliste.
On utilise généralement une instance
de cette classe en conjonction avec une instance de la classe
FilteredImageSource pour produire une image
redimensionnée.
Image src = getImage(...);
ImageFilter colorfilter = new UnFiltreQuelconque();
Image img = createImage(new FilteredImageSource(src.getSource(), colorfilter);
|
- AreaAveragingScaleFilter : sous classe de la classe
ReplicateScaleFilter qui également de redimessioner une image avec un algorithme
différent du précédant et donne un rendu plus attenué.
public class AreaAveragingScaleFilter extends ReplicateScaleFilter
|
- BufferedImageFilter :
JDK-1.2TERMINER
- CropImageFilter : qui permet d'extraire une partie
rectangulaire d'une image et le fournit une source ne contenant que la partie
de l'image selectonnée. On utilise généralement une instance
de cette classe en conjonction avec une instance de la classe
FilteredImageSource.
ImageFilter cropfilter = new CropImageFilter(x0, y0, x1-x0, y1-y0);
ImageProducer prod = im.getSource();
prod = new FilteredImageSource(prod, cropfilter);
imm = createImage(prod);
|
- RGBImageFilter : qui permet de créer un filtre de couleurs.
On utilise généralement une instance
de cette classe en conjonction avec une instance de la classe
FilteredImageSource pour produire une image dont les couleurs
sont altérées selon ce que la méthode filterRGB décide de faire.
C'est une classe abstraite et il appartient au programmeur de définir
une classe dérivée spécifiant le type d'altération qu'il désire en
implantant la méthode filterRGB.
class EchangerRougeEtBleuFilter extends RGBImageFilter {
public int filterRGB(int x, int y, int rgb) {
return ((rgb & 0xff00ff00) | ((rgb & 0xff0000) >> 16) | ((rgb & 0xff) << 16));
}
}
|
import java.awt.*;
import java.applet.*;
import java.awt.image.*;
import java.awt.event.*;
public class Filtre extends Applet implements AdjustmentListener {
Image im ;
int x0=0, y0=0;
int x1=0, y1=0;
Image imm;
Button b1, b2;
CopieImage cp;
PlusOuMoinsRouge colorfilter ;
ImageProducer prodCrop;
public void init() {
cp = new CopieImage(this);
im = getImage(getCodeBase(), "pp.gif");
setForeground(Color.white);
addMouseMotionListener(
new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
x1 = e.getX(); y1 = e.getY(); repaint();
}
}
);
addMouseListener(
new MouseAdapter() {
public void mousePressed(MouseEvent e) {
x1 = x0 = e.getX(); y1 = y0 = e.getY();
}
public void mouseReleased(MouseEvent e) {
x1 = e.getX(); y1 = e.getY(); BoutDImage(); repaint();
}
}
);
}
public void update(Graphics g) {
paint(g);
cp.redessiner(imm);
}
public void paint(Graphics g) {
g.drawImage(im, 0, 0, this);
g.drawRect((x0 < x1) ? x0 : x1, (y0 < y1) ? y0 : y1, Math.abs(x1-x0), Math.abs(y1-y0));
}
void BoutDImage() {
ImageFilter cropfilter = new CropImageFilter((x0 < x1) ? x0 : x1, (y0 < y1) ? y0 : y1, Math.abs(x1-x0), Math.abs(y1-y0));
ImageProducer prod = im.getSource();
prodCrop = new FilteredImageSource(prod, cropfilter);
colorfilter = new PlusOuMoinsRouge();
prod = new FilteredImageSource(prodCrop, colorfilter);
imm = createImage(prod);
}
public static int i = 100;
public void adjustmentValueChanged(AdjustmentEvent e) {
i = e.getValue();
cp.redessiner(imm);
}
}
class CopieImage extends Frame {
Copie c;
ImageFilter colorfilter;
FilteredImageSource prod;
Filtre f;
Scrollbar scb;
public CopieImage(Filtre f) {
this.f = f;
setLayout(new BorderLayout());
c = new Copie(f);
add(c, "Center");
scb = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 360);
add(scb, "South");
scb.addAdjustmentListener(f);
setVisible(true);
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) { setVisible(false); }
}
);
}
void redessiner(Image imm) {
setVisible(true);
c.redessiner(f.imm);
}
}
class Copie extends Canvas {
Filtre f;
int x0 = 50, y0 = 50;
public Copie(Filtre f) {
this.f = f;
addKeyListener(
new KeyAdapter() {
public void keyTyped(KeyEvent e) {
switch (e.getKeyChar()) {
case 'a' : System.out.println("agrandir");
break;
case 'r' : System.out.println("reduire");
break;
case 'g' :
if (--x0 < 0) x0=0;
repaint();
break;
case 'd' :
x0++;
repaint();
break;
case 'h' :
if (--y0 < 0) y0=0;
repaint();
break;
case 'b' :
y0++;
repaint();
break;
}
}
}
);
}
void redessiner(Image imm) { repaint(); }
public void paint(Graphics g) {
if (f.imm != null) g.drawImage(f.imm, x0, y0, this);
else g.drawString("Aucune portion d'image selectionnée", 10, 10);
}
}
class PlusOuMoinsRouge extends RGBImageFilter {
public int filterRGB(int x, int y, int rgb) {
return (rgb & 0xff000000) | ((rgb & 0x00000ff) << 16)
| (rgb & 0x0000ff00) | ((rgb & 0x00ff0000) >> 16) ;
}
}
|
A TERMINER
Next: 31 Le son
Up: Java: Programmation graphique
Previous: 29 Couleurs et Fontes
Touraivane
6/12/1998