Ecco una guida su come gestire le immagini con java, la guida si suddivide in

- Creare un'immagine vuota
- Caricare un'immagine esistente e visualizzare
- Usare il doppio buffering e MediaTracker

Caricare un'immagine vuota
------------------------------
Per creare un oggetto immagine vuota si usa la classe Image, Canvas e il metodo createImage; vediamo un esempio:

/**/
...
Canvas c = new Canvas();
Image immagine = c.createImage(300, 500);
...
/**/

i parametri passati sono le dimensioni dell'immagine, naturalmente rettangolare quindi nell'esempio 300 di base e 500 di altezza. Quest'immagine appena creata sarà vuota e sarà possibile usarla come buffer per settare i 150 mila pixel(300 * 500) manualmente.

Creare un'immagine esistente da file
--------------------------------------
Per prendere un'immagine invece già pronta e saltata su file system si usa il metodo getImage, vediamone un esempio:

/**/
...
Image img = getImage(getDocumentBase(), "nomeImmagine.jpg");
//se non è un applet si pul usare
Image img = getImage("pathCompleto/NomeImmagine.jpg");
...
/**/

La visulizzazione di un'immagine, sia per quelle da file che quelle create programmaticamente in memoria, si usa drawImage:

/**/
...
Graphics g = componenteContenitore.getGraphics();
g.drawImage(nomeoggettoimage, 0, 0, this);
//se è un applet:
public void paint(Graphics g) {
g.drawImage(nomeoggettoimage, 0, 0, this);
}
...
/**/

Classi per gestire in modo avanzato le immagini in Java
====================================
Vedremo ora un insieme di classi che permetteranno di ricevere notifiche sulle modifiche ad un immagine, disegnarne programmaticamente e il doppio buffering per caricare e visualizzare immagini solo quando sono complete evitando sfarfallii.

ImageObserver
----------------
Questa interfaccia permette di ricevere notifiche sul caricamento di un'immagine; il metodo da utilizzare è imageUpdate, vediamo un primo esempio che ci dice se un'immagine è stata caricata completamente oppure no:

/**/
...
public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {
if((flags & ALLBITS) == 0) {
//se è vero l'immagine sta ancora caricando
return true;
} else {
//s sei qui il caricamente è terminato
}
}
...
/**/

il flags lo si può paragonare anche ad altre costanti(per più info rimando alla documentazione Java di Sun) cmq l'utilità di quest'interfaccia non finisce qui, il prossimo esempio mostra come mostrare in real time ogni insieme di pixel che arrivano vedendo pian piano crearsi l'immagine e nel caso di errore mostrare un messaggio:

/**/
...
public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {
if((flags & SOMEBITS) != 0) {
//se è vero ci sono nuovi pixel da inserire nel monitore con:
repaint(x, y, w, h);
} else if((flags & ABORT) != 0){
//se sei qui è stata interrotto il caricamento causa errore, topo file non trovato
}
return (flags & (ALLBITS|ABORT) == 0);//ritorna vero o falso a seconda dell flag
}
...
/**/

Con la tecnica vista sopra vedremo il caricamento di ogni porzione che viene presa dal programma, se volessimo invece visualizzare l'immagine solo a caricamento terminato dovremmo usare un codice come questo:

/**/
...
public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {
if((flags & ALLBITS) != 0) {
repaint();
} else if((flags & ABORT|ERROR) != 0){
//se sei qui è stato interrotto il caricamento causa errore, tipo file non trovato
}
return (flags & (ALLBITS|ABORT|ERROR) == 0);//ritorna vero o falso a seconda dell flag
}
...
/**/


Questa tecnica riduce lo sfarfallio ma c'è un sistema più efficace: il doppio buffering, vediamo come sfruttarlo.

ATTENZIONE: Molti principianti in Java magari solo nel campo delle immagini hanno trovato parecchio ridondante, complicata e dispersiva la tecnica di ImageObserver, quindi è stato chiesto a Sun una soluzione più efficace sopratutto quando ci sono più immagini da gestire, la vedremo però più avanti: la classe MediaTracker.

Doppio buffering
-----------------
All'inizio abbiamo visto come creare un'immagine vuota, ora vedremo come sfruttarla da "secondo monitor" e avere una sorta di buffer dove salvare l'immagine caricata e visualizzarla all'utente solo quando sarà pronta(evitando sfarfallii), vediamo come scrivere su un'immagine in memoria e risvuotarla a video solo completa:

/**/

//usare il tag html <applet code="test.class" witdh=300 height=400></applet>

import java.awt.*;
import java.awt.image.*;
import java.applet.*;

public class test extends Applet {
Image testt = null;


public void init() {
testt = createImage(300, 400);
}

public void paint(Graphics g) {
Graphics buffer = testt.getGraphics();
buffer.setColor(Color.red);
buffer.drawLine(0, 0, 300, 400);
buffer.setColor(Color.blue);
buffer.drawLine(300, 0, 0, 400);
buffer.setColor(Color.black);
buffer.drawString("Ciao, sono un esempio di doppio buffering", 50, 200);
g.drawImage(testt, 0, 0, null);
}
}
/**/

L'immagine sarà una X con scritto al centro "Ciao, sono un esempio di doppio buffering", ma la vedremo solo quando sarà pronta.


MediaTracker
----------------
Parlando prima di ImageObsever abbiamo nominato la suddetta classe nel titolo che permette di gestire meglio le notifiche delle immagini e in modo più semplice anche se sono molteplici di numero.

La prima cosa da fare è inserire nella classe le immagini da controllare col metodo addImage() dove si passa l'oggetto image il un ID intero, poi si controlla con il metodo checkID(int ID) che ritorna true se l'immagine con quell'ID è stata caricata sennò false; vediamo un esempio:

/**/
...
MediaTracker t = new MediaTracker();
Image immagini[5];
immagini[0] = getImage("path e nome");
immagini[1] = getImage("path e nome");
immagini[2] = getImage("path e nome");
immagini[3] = getImage("path e nome");
immagini[4] = getImage("path e nome");
for(int i = 0; i < 5; i++)
t.addImage(immagini[i], i);//le 5 immagini verranno controllare da MediaTracker
...
public void paint(Graphics g) {
for(int j = 0; j < 5;  j++) {
if(t.checkID(j)) {
//l'immagine con id i è stata caricata fino alla fine
}
//l'immagine è ancora in caricamento
}
}
...
/**/

Ecco, spero di aver chiarito come funziona questa classe che per fare ad esempio una barra di scorrimento o altre utility che informano l'utente sullo stato di caricamento dell'applet(in questo caso la fase che carica le immagini) è molto efficace.

/**/
import java.awt.*;
import java.awt.image.*;
import java.applet.*;

public class test extends Applet {
Image testt[] = new Image[3];
MediaTracker t = null;
int posx = -199;
int posy = 1;
int strpos = 200;
Thread tt = null;

public void start() {
tt = new Thread(new Runnable() {
public void run() {
tt.setPriority(Thread.MIN_PRIORITY);
try {
while(true) {
repaint();
Thread.sleep(500);
}
} catch(Exception e) {
e.printStackTrace();
}
}
});

tt.start();

}

public void init() {
t = new MediaTracker(this);
testt[0] = getImage(getDocumentBase(), "lupo-1.jpg");
testt[1] = getImage(getDocumentBase(), "lupo-2.jpg");
testt[2] = getImage(getDocumentBase(), "lupo-3.jpg");

for(int i = 0; i < 3; i++) {
t.addImage(testt[i], i);
}
}

public void paint(Graphics g) {
int done = 0;

for(int i = 0; i < 3; i++) {
if((t.checkID(i, true))) {
strpos+=50;
g.drawString("Caricamento terminato per immagine "+i, 100, strpos);
posx+=300;
g.drawImage(testt[i], posx, posy, this);
done++;
if(done == 3) {
tt.stop();
}
}

else {
strpos+=50;
g.drawString("Sto caricando immagine... "+i, 100, strpos);
}
}
}

}
/**/

Ecco, questa era la prima parte della guida nelle successive vedremo ImageProducer, ImageConsumere tutte quelle classi che permettono la modifica di un'immagine da file  o la creazione dettagliata a livello di pixel come array di bytes.