42
Buenas tardes este es el código que he creado para obtener un visor de imágenes: import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.io.File; import javax.swing.JPanel; public class PanelImage extends JPanel{ private Image imagen; public PanelImage(Image imagen,int ancho, int alto ){ this.imagen=imagen; this.setSize(new Dimension (ancho, alto)); }//constructor public Image getImagen(){ return imagen; }//nos permite consultar imagenes public void setImagen(Image imagen){ this.imagen=imagen; this.repaint(); }//cambia imagen public void setIMagenFromFile(File file){ this.imagen=Toolkit.getDefaultToolkit().getImage(file.getAbsolutePath()); this.repaint(); }// public void paintComponent(Graphics g){ super.paintComponent(g); g.drawImage(this.getImagen(),0,0,this.getWidth(),this.getHeight(),this); }// }//class

codigo imagenes

Embed Size (px)

Citation preview

Page 1: codigo imagenes

Buenas tardes este es el código que he creado para obtener un visor de imágenes: 

import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.io.File; 

import javax.swing.JPanel; 

public class PanelImage extends JPanel{ 

private Image imagen; 

public PanelImage(Image imagen,int ancho, int alto ){ this.imagen=imagen; this.setSize(new Dimension (ancho, alto)); 

}//constructor 

public Image getImagen(){ return imagen; }//nos permite consultar imagenes 

public void setImagen(Image imagen){ this.imagen=imagen; this.repaint(); 

}//cambia imagen 

public void setIMagenFromFile(File file){ this.imagen=Toolkit.getDefaultToolkit().getImage(file.getAbsolutePath()); this.repaint(); 

}// 

public void paintComponent(Graphics g){ super.paintComponent(g); g.drawImage(this.getImagen(),0,0,this.getWidth(),this.getHeight(),this); 

}// 

}//class

Page 2: codigo imagenes

import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.event.*;import java.util.*;

//Frames.

//SplitPaneDemo itself is not a visible component.public class SplitPaneDemo implements ListSelectionListener { //Visor de imagenes.private Vector imageNames;private JLabel picture; //la imagen q se mostrara.private JList list; //la lista de eleccion de imagenes.private JSplitPane splitPane; //Divide la ventana.

public SplitPaneDemo() { //Constructor.//Leer los nombres de imágenes desde un archivo de propiedades.ResourceBundle imageResource; 

try {imageResource = ResourceBundle.getBundle("imagenames" ; //Indicamos el archivo imagenames.properties --> images=, es decir una variable que contiene parametros o valores.

String imageNamesString = imageResource.getString("images" ;//Indicamos el parametro de referencia de los datos a recoger. Recoge los nombres de images=, es decir: "...jpg ...jpg ...jpg"imageNames = parseList(imageNamesString);} //Guardamos en el Vector los valores del vector 'v' de el metodo --> protected static Vector parseList(String theStringList). (que se encuentra mas abajo, en el programa).catch (MissingResourceException e) {System.out.println("No se puede encontrar el archivo de propiedades que contiene los nombres de las

imágenes" ; }

//Crear la lista de imágenes y ponerla en un ScrollPane.list = new JList(imageNames); //Inicializamos la lista pasandole los nombres de las imagenes contenidas en el vector imageNames (Ya separadas).list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); //Establecer el mode de seleccion, tipo simple, solo una opcion seleccionada.list.setSelectedIndex(0); //Por defecto aparecera seleccionado el primer elemento de la lista.list.addListSelectionListener(this); //Creamos un oyente para la lista.JScrollPane listScrollPane = new JScrollPane(list); //Metemos la lista en un

Page 3: codigo imagenes

JScrollPane (Panel con barras de desplazamiento). 

//Configura la picture label y la coloca en el ScrollPaneImageIcon firstImage = new ImageIcon("images/" +(String)imageNames.firstElement());//Creamos una imagen, y le decimos que su valor inicial sea el nombre del primer elemento del vector convertido a string. Esto es para decirle que la primera imagen esta en la carpeta images/nombredelprimerelemento, es decir --> images/francia.jpg.picture = new JLabel(firstImage); //Inicializamos la etiqueta con esa imagen.picture.setPreferredSize(new Dimension(firstImage.getIconWidth(),firstImage.getIconHeight())); //Ponemos un tamaño a la etiqueta. La etiqueta tiene el tamaño de la imagen que contendra.JScrollPane pictureScrollPane = new JScrollPane(picture); //Metemos en un JScrollPane la imagen.

//Crear un SplitPane con dos ScrollPane en él.splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);//Divimos la ventana en horizontal (Una linea vertical).splitPane.setLeftComponent(listScrollPane); //En el lado izquierdo pon lo el listScrollPane.splitPane.setRightComponent(pictureScrollPane); //En el lado derecho pon lo el pictureScrollPane.splitPane.setOneTouchExpandable(true); //Añade la opcion de expandir.

//Configurar el tamaño mínimo para los dos componentes en el SplitPane.Dimension minimumSize = new Dimension(100, 50); //Define el tamaño minimo.listScrollPane.setMinimumSize(minimumSize); //Tamaño minimo de el listScrollPane.pictureScrollPane.setMinimumSize(minimumSize); //Tamaño minimo de el pictureScrollPane.

//Establecer la localización y el tamaño iniciales del divisor.splitPane.setDividerLocation(150); //La barra de division se encontrara en la posicion... --> 150.splitPane.setDividerSize(10); //La barra de division tendra un grosor de... --> 10.

//Configurar el tamaño preferido para el SplitPane.splitPane.setPreferredSize(new Dimension(400, 200));}//Fin-Constructor.

public JSplitPane getSplitPane() {return splitPane;} //Metodo que devuelve el

Page 4: codigo imagenes

valor del splitPane.

public void valueChanged(ListSelectionEvent e) {//Metodo que va ha ser llamado cada vez que ocurra un cambio en la lista.if (e.getValueIsAdjusting()) //Devuelve 'true' si ha ocurrido multiples eventos (se ha seleccionado varios elementos).return; //Sale del metodo. Devuelve el control del programa al main (que es quien lo llamo).

JList theList=(JList)e.getSource(); //Creamos un objeto JList donde le pasamos el valor e.getSource(), que significa obtener la fuente, y la convierte a tipo JList.

if (theList.isSelectionEmpty()) //Si theList tiene una seleccion vacia.picture.setIcon(null); //No pongas ninguna imagen.else{int index=theList.getSelectedIndex(); //Le pasamos el indice seleccionado de la lista.ImageIcon newImage = new ImageIcon("images/"+(String)imageNames.elementAt(index)); //Le indicamos la imagen que queremos que se muestre.picture.setIcon(newImage); //Enviamos la imagen que se encuentra en newImage.picture.setPreferredSize(new Dimension(newImage.getIconWidth(),newImage.getIconHeight() )); //Preferencia de tamaño de imagen.picture.revalidate(); //Refresca la etiqueta.}}//Fin-valueChanged.

protected static Vector parseList(String theStringList) { //Le pasamos la cadena sin separar. --> "francia.jpg espana.jpg italia.jpg alemania.jpg"Vector v = new Vector(10); //Creamos un vector con 10 posiciones.

StringTokenizer tokenizer = new StringTokenizer(theStringList, " " ;//la cadena que keremos separar y el " " (espacio) que es lo que actua como separador.while (tokenizer.hasMoreTokens()){ //Divide las cadenas y los añade a el vector 'v'.String image = tokenizer.nextToken();v.addElement(image);}return v; //Devuelve 'v'. Esto es lo que dara el valor al otro vector, imageNames.}//fin-parseList.

//Crea la ventana y muestra el contenido.

Page 5: codigo imagenes

public static void main(String s[]) { 

JFrame frame=new JFrame("SplitPaneDemo" ; //Creamos una Frame, pasandole como valor un nombre que aparecera en la barra de titulo.frame.addWindowListener(new WindowAdapter(){ //Añadimos un oyente para el frame.public void windowClosing(WindowEvent e)//Evento para cuando se cierre la ventana.{System.exit(0);}});SplitPaneDemo splitPaneDemo=new SplitPaneDemo(); //Creamos un objeto de la clase SplitPaneDemo (La cual creamos en este programa).frame.getContentPane().add(splitPaneDemo.getSplitPane()); //Obtener el contenedor de la ventana. --> Container contenedor=frame.getContentPane(); //Añadir al contenedor --> contenedor.add(splitPaneDemo.getSplitPane());//splitPaneDemo.getSplitPane() porque no podemos llamar a splitPane directamente, //ya que el main es estatico y en ningun sentido podriamos conseguirlo, //por lo que creamos un objeto de la clase SplitPaneDemo y asi podremos utilizar //el metodo getSplitPane() que devuelve el valor del splitPane.

frame.pack(); //Dimensiona la ventana para que se ajuste a su contenido.frame.setVisible(true); //Hacer visible el frame.}//Fin-main.}//Fin.                                                                                                                    

La clase Graphics, Métodos para ImágenesAnterior | Siguiente

Métodos de Clipping

La palabra Clipping está muy arraigada entre los programadores gráficos, sin tener una contrapartida en español adecuada, ya que quizá la que más se asemeje sea recorte, pero tampoco es demasiado precisa; por ello, el autor va a seguir utilizando el término sajón, aún a pesar de la reticencia de los puristas del español. Clipping es el proceso por el que se define la zona del contexto gráfico en la que se van a realizar las modificaciones de los subsiguientes procesos de dibujo. Cualquier pixel que se encuentre fuera del área de clipping permanecerá inmune a toda modificación.

Los métodos que están involucrados son varios, unos que permiten fijar el rectángulo de clipping y otros que permiten obtener la zona de clipping actual.

clipRect( int,int,int,int ), realiza la intersección del rectángulo de clipping actual con el

Page 6: codigo imagenes

formado por los parámetros que se pasan al método. Este método solamente se puede utilizar para reducir el tamaño de la zona de clipping, no para aumentarla.

getClip(), devuelve la zona de clipping actual como un objeto de tipo Shape. No obstante, Shapees un interface, por lo que hay que tener cuidado en su uso, y además consultar la documentación delAPI Java2D, en donde ha sido completamente revisada, así que lo mejor es evitar el uso de este interfaz en el código propio.

getClipBounds(), devuelve el rectángulo que delimita el borde de la zona de clipping actual.

setClip (int,int,int,int ), fija el rectángulo de clipping actual al indicado en las cuatro coordenadas que se pasan como parámetro al método.

Métodos para manejo de Imágenes

Hay varias formas distintas para el método drawImage(), que difieren fundamentalmente en las posibilidades de manipulación del método y los parámetros que hay que pasar para realizar esas manipulaciones. La siguiente llamada es solamente representativa del grupo, que se verán posteriormente.

drawImage( Image,int,int,int,int,int,int,int,int,Color,ImageObserver ), pinta sobre la zona indicada la imagen que se pasa como parámetro, escalándola sobre la marcha para que rellene completamente el área de destino.

No obstante, antes de entrar en el desarrollo de los diferentes métodos drawImage(), quizá sea presentar la clase Color, que es relativamente simple de entender y utilizar.

La clase Color

Esta clase es simple, encapsula los colores utilizando el formato RGB. En este formato, los colores se definen por sus componentes Rojo, Verde y Azul, representado cada uno de ellos por un número entero en el rango 0-255. En el caso del API Java2D, el espacio de color que se usa es el sRGB, un estándar del consorcio W3.

Hay otro modelo de color llamado HSB (matiz, saturación y brillo). La clase Color proporciona métodos de conveniencia para poder realizar la conversión entre un modelo y otro sin dificultad.

La clase Color proporciona variables estáticas finales que permiten el uso de cualquiera de los treinta colores, simplemente especificando su nombre. Para usar estas variables sólo es necesario referencias el color por su nombre de variable, por ejemplo

objeto.setBackground( Color.red );

Los valores predefinidos para los colores en esta clase son los que se muestran en la siguiente lista, todos ellos en el espacio sRGB:

black, negro blue, azul cyan, azul celeste darkGray, gris oscuro

Page 7: codigo imagenes

gray, gris green, verde lightGray, gris claro magenta, violeta orange, naranja pink, rosa red, rojo white, blanco yellow, amarillo

Los constructores de esta clase son tres, dos de los cuales permiten instanciar un nuevo objeto de tipo Color indicando las cantidades de rojo, verde y azul que entran en su composición mediante valores enteros en el rango 0 a 255:

Color( int,int,int )

El otro de los constructores que permite especificar la contribución de rojo, verde y azul al color final, admite valore flotantes en el rango 0.0 a 1.0:

Color( float,float,float )

Y el tercer constructor admite solamente un número entero en el cual se especifica el valor RGB, de forma que la cantidad de color rojo que interviene en el color final está especificada en los bits 16-23 del argumento, la cantidad de verde en los bits 8-15 y la cantidad de azul en los bits 0-7.

Color( int )

Para el uso con los colores, JDK dispone de una serie de métodos, de los cuales JavaSoft proporciona una completa descripción en la documentación del AWT. Aquí se intentan englobar en base a su funcionalidad.

Métodos que devuelven un entero representando el valor RGB para un determinado objeto Color.

getRed(), devuelve el componente rojo del color como un entero en el rango 0 a 255.

getGreen(), devuelve la cantidad de verde que entra en la composición del objeto Color, en el rango 0 a 255.

getBlue(), devuelve el componente azul del color con un entero en el rango 0 a 255.

getRGB(), devuelve un entero representando el color RGB, utilizando los bits para indicar la cantidad de cada uno de los componentes rojo, verde y azul que entran en su composición. Los bits 24 a 31 del entero que devuelve el método son 0xff, los bits 16 a 23 son el valor rojo, los bits 8 a 15 son el valor verde y los bits 0 a 7 indican el valor del color azul. Siempre en el rango 0 a 255.

Los siguiente métodos devuelven un objeto de tipo Color, de forma que se pueden utilizar para crear objetos de este tipo.

brighter(), crea una versión más brillante del color.

darker(), crea una versión más oscura del color.

Page 8: codigo imagenes

decode( String ), convierte una cadena a un entero y devuelve el color correspondiente.

Los métodos que se indican a continuación, también devuelven un objeto de tipo Color, pero están especializados para trabajar con el sistema a través de la clase Properties.

getColor( String ), busca un color entre las propiedades del sistema. El objeto String se utiliza como el valor clave en el esquema clave/valor utilizado para describir las propiedades en Java. El valor es entonces utilizado para devolver un objeto Color.

getColor( String,Color ), busca un color entre las propiedades del sistema. El segundo parámetro es el que se devuelve en el caso de que falle la búsqueda de la clave indicada en el objeto String.

getColor( String,int ), busca un color entre las propiedades del sistema. El segundo parámetro es utilizado para instanciar y devolver un objeto Color en el caso de que falle la búsqueda de la clave indicada en el objeto String.

Los siguientes métodos se utilizan para realizar la conversión entre el modelo de color RGB y el modeloHSB.

getHSBColor( float,float,float ), crea un objeto de tipo Color basado en los valores proporcionados por el modelo HSB.

HSBtoRGB( float,float,float ), convierte los componentes de un color, tal como se especifican en el modelo HSB, a su conjunto equivalente de valores en el modelo RGB.

RGBtoHSB( int,int,int,float[] ), convierte los componentes de un color, tal como se especifican en el modelo RGB, a los tres componentes del modelo HSB.

Y ya, los métodos que quedan son métodos de utilidad general, sin ninguna aplicación específica en lo que al Color respecta.

equals( Object ), determina si otro objeto es igual a un color.

hashCode(), calcula el código hash para un color.

toString(), crea una cadena representando al color, indicando el valor de sus componentes RGB.

El método drawImage()

Este método es el que se utiliza para presentar una imagen en pantalla; aunque hay métodos de otras clases que son utilizados para la manipulación de imágenes en Java, lo primero que se necesita es saber cómo se visualiza una imagen, y eso es lo que hace este método drawImage() de la claseGraphics.

Hay varias versiones sobrecargadas de este método, algunas de las cuales se muestran a continuación, aunque el lector debe referirse a la documentación del API de Java para disponer de información detallada de cada uno de estas formas.

drawImage( Image,int,int,Color,ImageObserver ), pinta la imagen que se indica situando su esquina superior izquierda en la coordenada que se pasa, en el contexto gráfico actual. Los pixeles

Page 9: codigo imagenes

transparentes se pintan en el color de fondo que se indica. Esta operación es equivalente a rellenar un rectángulo del ancho y alto de la imagen dada con un color y luego pintar la imagen sobre él, aunque probablemente más eficiente.

drawImage( Image,int,int,ImageObserver ), pinta la imagen que se indica situando su esquina superior izquierda en la coordenada que se pasa, en el contexto gráfico actual. Los pixeles transparentes de la imagen no se ven afectados. Este método retorna inmediatamente en todos los casos, incluso aunque la imagen completa no se haya terminado de cargar y no se haya presentado completamente en el dispositivo de salida.

drawImage( Image,int,int,int,int,Color,ImageObserver ), pinta la imagen que se pasa dentro del rectángulo que se indica en los parámetros, escalando esa imagen si es necesario. El método retorna inmediatamente, aunque la imagen no se haya cargado completamente. Si la representación de la imagen en el dispositivo de salida no se ha completado, entonces drawImage() devuelve el valor false.

drawImage( Image,int,int,int,int,ImageObserver ), pinta la imagen que se pasa dentro del rectángulo que se indica en los parámetros, escalando esa imagen si es necesario. El método retorna inmediatamente, aunque la imagen no se haya cargado completamente. Si la representación de la imagen en el dispositivo de salida no se ha completado, entonces drawImage() devuelve el valor false. El proceso que pinta las imágenes notifica al observador a través de su método imageUpdate().

drawImage( Image,int,int,int,int,int,int,int,int,Color,ImageObserver ).

drawImage( Image,int,int,int,int,int,int,int,int,ImageObserver ).

Como puede comprobar el lector, se permite mucha flexibilidad a la hora de presentar imágenes, aunque quizá necesite una descripción más detallada de la llamada a cada método, por ejemplo, la descripción que encontrará en el API para una de las versiones del método es:

public abstract boolean drawImage( Image img, int x, int y, int width, int height, ImageObserver observer )

Los parámetros del método son:

img, la imagen que se quiere pintarx e y, coordenadas para posicionar la esquina superior izquierda de la imagenwidth y height, dimensiones del rectángulo sobre el que se posicionará la imagenobserver, objeto que ha de ser notificado tan pronto como la imagen se haya convertido y situado en su posición

En esta versión del método, la imagen se pintará dentro del rectángulo que se para en los parámetros y se escalará si es necesario. El método retorna inmediatamente, incluso aunque la imagen no se haya terminado de escalar o convertir al formato que admita el dispositivo de salida. Si esta conversión no se ha completado, drawImage() devuelve false, y como hay parte de imagen que no está procesada, el proceso que pinta la imagen notifica al observador a través de su

Page 10: codigo imagenes

método imageUpdate() y esa imagen continúa pintándose por trozos.

El método imageUpdate() pertenece a la clase Component, cuando es llamado, encola una llamada arepaint(), permitiendo que se pinta un trozo más de imagen sin que se interfiera la tarea o hilo de control principal. Hay también una propiedad, awt.image.incrementaldraw que determina si la imagen puede ser pintada en piezas, tal como se ha indicado antes. El valor por defecto para esta propiedad es true, en cuyo caso el sistema va pintado trozos de imagen según van llegando. Si el valor es false, el sistema esperará a que la imagen esté completamente cargada antes de pintarla.

Hay una segunda propiedad, awt.image.redrawrate que determina el período mínimo, en milisegundos, que debe haber entre llamadas a repaint() para imágenes. El valor por defecto es 100, y solamente se aplica cuando la propiedad anterior es true. Tanto esta propiedad como la anterior se pueden modificar, pero solamente sirve esa modificación para la ejecución actual del programa.

En el ejemplo java1518.java se muestra el efecto de pintar una imagen en trozos, según van estando disponibles, además del escalado de esa imagen. La salida visual que se genera no es demasiado satisfactoria porque tiende a producir un cierto parpadeo al realizar varias llamadas al métodoimageUpdate() durante el proceso de pintado de la imagen. No obstante, esto se corregirá en el ejemplo siguiente, java1519.java, con el uso de un objeto de tipo MediaTacker.

Según la documentación de JavaSoft, la versión escalada de una imagen no necesariamente estará disponible inmediatamente, porque antes debe construirse una versión en dimensiones normales para el dispositivo de salida, sin embargo, como podrá comprobar el lector en el ejemplo java1519.java, este efecto aparentemente no se produce.

De los parámetros que se pasan al método drawImage(), todos resultan obvios, excepto quizá, el último de ellos, observer, que se refiere al objeto que debe ser notificado cuando la imagen esté disponible. Cuando se realiza una llamada a drawImage(), se lanza una tarea que carga la imagen solicitada. Hay un observador que monitoriza el proceso de carga; la tarea que está cargando la imagen notifica a ese observador cada vez que llegan nuevos datos. Para este parámetro se puede utilizar perfectamente this como observador en la llamada a drawImage(); es más, se puede decir que cualquier componente puede servir como observador para imágenes que se pintan sobre él. Quizá sea este el momento de entrar un poco más en detalle en la clase ImageObserver, e incluso antes en la clase Image, porque son los dos parámetros diferentes del típico entero que aparecen en la llamada adrawImage().

La clase Image

La clase Image, como cualquier otra, está formada por una serie de constantes o variables, constructores y métodos. Uno de estos métodos es getScaledInstance(), que devuelve una versión escalada de una imagen. Uno de los parámetros de este método es un valor entero que especifica el algoritmo a utilizar para la realización del escalado. Se puede utilizar cualquiera de ellos, la tabla siguiente muestra su nombre y una pequeña descripción, si el lector necesita más detalles, debe recurrir como siempre se recomienda, a la documentación del JDK.

SCALE_AREA_AVERAGING, utiliza el algoritmo Area AveragingSCALE_FAST, utiliza un algoritmo que proporcione mayor rapidez en el escalado, a costa de la suavidad de realización de ese escaladoSCALE_SMOOTH, al contrario que el anterior, sacrifica la velocidad por la suavidad de realización del escaladoSCALE_REPLICATE, utiliza un algoritmo de los internos de la clase ReplicateScaleFilterSCALE_DEFAULT, utiliza el algoritmo de defecto

Page 11: codigo imagenes

UndefinedProperty, un objeto de este tipo debe ser retornado siempre que una propiedad no se encuentre definida para una determinada imagen. Esta propiedad no se utiliza para el escalado, sino que es utilizada como valor de retorno del método getProperties() para indicar que la propiedad solicitada no está disponible.

Aunque la clase Image dispone de un constructor, es una clase abstracta, por lo que no se puede instanciar ningún objeto llamando directamente a este constructor. Se puede conseguir un objetoImage indirectamente por la invocación del método getImage() de las clases Applet o Toolkit. El método getImage() utiliza un hilo de ejecución, o tarea, separado para cargar la imagen. El resultado práctico de la invocación a getImage(), es la asociación entre una referencia de tipo Image y un fichero localizado en algún lugar que contiene la imagen que interesa; en este Tutorial se utilizan imágenes que están en el disco local, pero no se impide que puedan encontrarse en cualquier servidor de la Red.

Otra forma de obtener un objeto Image es invocar al método createImage(), de las clasesComponent y Toolkit. La lista siguiente muestra una pequeña descripción de los métodos de la claseImage; la documentación de JavaSoft puede proporcionar al lector una descripción más completa.

flush(), libera todos los recursos que utiliza el objeto Image

getGraphics(), crea un contexto gráfico para pintar una imagen en segundo plano

getHeight( ImageObserver ), determina la altura de la imagen

getWidth( ImageObserver ), determina la anchura de la imagen

getProperty( String,ImageObserver ), obtiene una propiedad de la imagen por su nombre

getScaledInstance( int,int,int ), crea una versión escalada de la imagen

getSource(), devuelve el objeto que produce los pixeles de la imagen

Algunos de estos métodos se utilizarán en los ejemplos que se presentan. No obstante, el interés no debe centrarse solamente en la clase Image, sino también en las otras clases que se necesitan para instanciar los parámetros de llamada a estos métodos, e incluso, algunas otras clases de soporte general. Por ello, aún a costa de resultar un poco ladrillo, se presenta a continuación una pequeña revisión de las clases más interesantes, MediaTracker e ImageProducer, ya que sobre las clasesString, ImageObserver, Graphics y Object, que son las que intervienen en los métodos de la clase Image, ya debería tener el lector alguna referencia; no obstante, si desea ampliar detalles sobre cualquiera de las clases e interfaces que dan soporte a los métodos de la clase Image, siempre queda el recurso de otros libros, tutoriales y, pro supuesto, la documentación de JavaSoft.

El interfaz ImageProducer

Se trata de un interfaz para objeto que pueden producir imágenes para la clase Image. Cada imagen contiene un ImageProducer que se utiliza en la reconstrucción de esa imagen cuando es necesario; por ejemplo, cuando se escala la imagen, o cuando se cambia la imagen de tamaño. Este interfaz declara varios métodos, algunos de los cuales se verán un poco más adelante.

La clase MediaTracker

Page 12: codigo imagenes

Esta es una clase de utilidad general diseñada para controlar el estado de los objetos de tipo media, que en teoría pueden ser tanto clips de sonido como cualquier otro objeto media, como es el caso de una imagen. Por ahora, en el JDK 1.2 solamente se soporta el control de imágenes.

Un objeto MediaTracker se utiliza a través de una instancia de la clase MediaTracker sobre el objeto Component que se quiere monitorizar, e invocando al método addImage() para cada una de las imágenes que se quiere controlar. A cada una de estas imágenes se puede asignar un identificador único, o también se puede asignar el mismo identificador a un grupo de imágenes. Este identificador controla el orden de prioridad en que se cargarán las imágenes, de forma que imágenes con un identificador bajo tienen preferencia sobre otras con identificador más alto. El identificador también se puede utilizar para identificar un conjunto único de imágenes. En otras palabras, asignando el mismo identificador a varias imágenes, se las puede manejar a la vez.

Se puede determinar el estado de una imagen (o grupo de imágenes) invocando alguno de los distintos métodos sobre el objeto MediaTracker, pasándole como parámetro el identificador de la imagen (o grupo de imágenes).

El objeto MediaTracker también se puede utilizar para bloquear la ejecución a la espera de la carga completa de una imagen (o grupo de imágenes), tal como se verá en alguno de los ejemplos, en que se espera a que la carga de las imágenes se realice completamente antes de presentarlas en pantalla.

La clase MediaTracker proporciona cuatro constantes, que se utilizan como valores de retorno de algunos de los métodos de la clase y, tal como su nombre sugiere, indican el estado en que se encuentra una imagen dada. Estas constantes son las siguientes.

ABORTED, la carga de la imagen (o cualquier media) ha sido abortadaCOMPLETE, realizara satisfactoriamente la carga completaERRORED, se ha encontrado algún tipo de error en la cargaLOADING, se está cargando la imagen (o cualquier media)

Para esta clase solamente hay un constructor que pueda ser invocado a la hora de instanciar objetos que controlen el estado de algunas, o todas, las imágenes sobre un determinado componente.

MediaTracker( Component )

Sin embargo, el número de métodos disponibles para los distintos propósitos, sí que es bastante extenso. Se ha intentado agruparlos en categorías, pero se recuerda al lector, que lo que aquí se expone es una pequeña descripción de cada uno de ellos para poder tener una visión global del funcionamiento, en este caso de la manipulación de imágenes, y que sea sencilla de entender su función; pero si el lector va a utilizar estos métodos en sus propios programas, debe consultar la documentación del API del JDK que esté utilizando, donde encontrará toda la información que JavaSoft proporciona para cada uno de estos métodos.

Un objeto MediaTracker tiene la posibilidad de controlar el estado de algunas, o todas, las imágenes que están siendo cargadas para el objeto Component que se ha pasado como parámetro a la hora de instanciar ese objeto MediaTracker. Los métodos siguientes se utilizan para crear y mantener esta lista de imágenes. Cuando se añade una imagen a la lista, hay que proporcionar un identificador numérico para esa imagen, que será luego el que sea utilizado por otros métodos para poder indicar el estado en que se encuentra una imagen determinada.

addImage( Image,int ), incorpora una nueva imagen a la lista de imágenes que está siendo

Page 13: codigo imagenes

controlada por el objeto MediaTracker.

addImage( Image,int,int,int ), incorpora una imagen escalada a la lista.

removeImage( Image ), elimina la imagen indicada de la lista.

removeImage( Image,int ), elimina la imagen que corresponde al identificador que se pasa como parámetro de la lista.

removeImage( Image,int,int,int ), elimina la imagen que corresponde al ancho, alto e identificador, de la lista de imágenes.

El objeto MediaTracker se puede utilizar para hacer que la tarea, o hilo de ejecución, se bloquee hasta que una o más imágenes de su lista haya completado su carga. Esto se consigue con los siguientes métodos.

waitForAll(), inicia la carga de todas la imágenes controladas por el objeto MediaTracker; devuelve void.

waitForAll( long ), inicia la carga de todas la imágenes, devolviendo true si todas las imágenes se han cargado y false en cualquier otro caso.

waitForID( int ), inicia la carga de las imágenes que corresponden al identificador que se pasa; devuelve void.

waitForID( int,long ), inicia la carga de las imágenes que corresponden al identificador que se pasa; devuelve true si todas las imágenes se han cargado y false en cualquier otro caso.

Por supuesto, es posible utilizar métodos que no bloquean la tarea que carga las imágenes para comprobar el estado de una o más imágenes de la lista. Esto permite continuar haciendo el trabajo mientras las imágenes siguen cargándose. Estos métodos devuelven true o false para indicar si la carga es completa. Observará el lector que hay dos versiones sobrecargadas de cada uno de los métodos. La versión con el parámetro booleano comenzará la carga de cualquier imagen que no se esté cargando, en caso de que ese parámetro booleano sea true. La otra versión no iniciará la carga de ninguna imagen. Esta interpretación también es válida para el parámetro booleano de otros métodos de la clase que disponen de él.

checkAll() y checkAll( boolean ), comprueban si todas las imágenes que están siendo controladas por el objeto MediaTracker han finalizado la carga.

checkID( int ) y checkID( int,boolean ), comprueban si todas las imágenes que corresponden al identificador especificado han concluido su carga.

El hecho de que los métodos anteriores indiquen que la carga de las imágenes ha sido completa, no garantiza que esa carga esté libre de errores. Los siguiente métodos se utilizan para determinar si se ha producido algún problema durante la carga de las imágenes.

getErrorsAny(), devuelve una lista de todas las imágenes (o cualquier media) en los que se ha producido un error en la carga.

Page 14: codigo imagenes

getErrorsAny( int ), devuelve una lista de todas las imágenes correspondientes a un determinado identificador, en los que se ha producido un error en la carga.

isErrorAny(), comprueba el estado de error de todas las imágenes.

isErrorID( int ), comprueba el estado de error de todas las imágenes correspondientes a un determinado identificador.

Los dos siguientes métodos devuelven un valor entero formado por la operación OR entre los valores de estado de todas las imágenes que se requieren; en el primero de todas las que controla el objetoMediaTracker, y en el segundo de las que corresponden al identificador que se especifica.

statusAll( boolean )

statusID( int,boolean )

El ejemplo java1518.java, como se ha indicado en algún párrafo anterior, muestra la carga y pintado de una imagen sin utilizar la clase MediaTracker, que sí se utilizará en el ejemplo java1519.java. El ejemplo, no obstante, muestra el uso de la clase Toolkit y el método getImage(), para asociar un fichero de imagen que se encuentra en el disco duro local, con el nombre de una variable de referencia de tipo Image. También muestra el uso del método drawImage() para pintar la imagen cargado sobre un objeto Frame; el uso de getWidth() y getHeight() para determinar el tamaño de la imagen; el uso de translate() para eliminar el efecto de offset generado por los insets del objeto Frame; y algunos otros métodos que ya deben ser archiconocidos para el lector. El código completo del ejemplo es el que se reproduce a continuación.

import java.awt.*;import java.awt.event.*;

// Clase de control del ejemploclass java1518 extends Frame { // Referencia a la imagen Image imagen; // Constructor de la clase public java1518() { this.setTitle( "Tutorial de Java, Gráficos" ); this.setSize( 275,250 ); // Recogemos en la variable "imagen" el fichero de imagen que // se indica, y que se supone situado en el mismo directorio y // disco que la clase del ejemplo imagen = Toolkit.getDefaultToolkit().getImage( "muneco.gif" ); // Se hace visible el Frame, que en la pantalla da origen a // la ventana, aunque la primera imagen no es visible en el // mismo momento en que aparece la ventana en pantalla, porque // hasta que se invoque por primera vez el método paint(), no // se colocará una imagen en el contendor this.setVisible( true );

// Clase anónima anidada que permite terminar la ejecución del

Page 15: codigo imagenes

// programa, controlando el botón de cierre del Frame this.addWindowListener( new WindowAdapter() { public void windowClosing( WindowEvent evt ) { // Se sale al sistema System.exit( 0 ); } } ); } public static void main( String[] args ) { // Se instancia un objeto de la clase new java1518(); }

// Se sobrecarga el método para pintar la imagen public void paint( Graphics g ) { // Se traslada el origen para evitar el efecto del borde g.translate( this.getInsets().left,this.getInsets().top ); // Ahora se pinta la imagen a la mitad de su tamaño g.drawImage( imagen,0,0, imagen.getWidth(this)/2,imagen.getHeight(this)/2,this ); } }

La imagen siguiente reproduce la ventana generada en la ejecución del programa, que se repetirá en los demás ejemplos, en donde se utilizan distintas formas de presentar lo mismo, por lo que sirva de referencia al lector esta imagen para éste y subsiguientes ejemplos.

Como el lector también supondrá, lo que sigue ahora es la revisión de algunos de los trozos de código del ejemplo que resultan más interesantes. Aunque precisamente en este ejemplo, no sea especialmente interesante el detenerse porque es una simple presentación y toma de contacto con la clase Image, pero sí se pueden revisar aquellas partes que van a ser comunes en todos los ejemplos.

Así que, lo primero que debe observarse es la declaración de la variable de instancia que es una variable de referencia de tipo Image, que será la que posteriormente se utilice para manipular esa

Page 16: codigo imagenes

imagen.

class java1518 extends Frame { Image imagen; // Referencia a un objeto Image

El siguiente trozo de código que aquí debe resaltarse es la sentencia en el constructor del contenedor, que utiliza el método getImage() de la clase Toolkit para asociar el fichero muneco.gif con la variable de referencia imagen.

imagen = Toolkit.getDefaultToolkit().getImage( "muneco.gif" );

Es importante hacer notar al lector que esta sentencia no hace que la imagen aparezca en pantalla en el momento en que el contenedor se haga visible.

Las líneas de código que puedan interesar hay que buscarlas ya en el método paint(), en donde se invoca una de las versiones sobrecargadas del método drawImage(), al cual se le indica en la lista de parámetros que se le pasa; en primer lugar, el nombre de la variable de referencia que apunta al fichero en disco que contiene la imagen, luego las coordenadas donde se debe colocar la esquina superior izquierda de la imagen, después el ancho y alto que debe tener la imagen, y por fin, thiscomo objeto observador.

// Ahora se pinta la imagen a la mitad de su tamañog.drawImage( imagen,0,0, imagen.getWidth(this)/2,imagen.getHeight(this)/2,this );

Los métodos getWidth() y getHeight() de la clase Image, se han utilizado para determinar el tamaño original de la imagen, y hacer que se presente en pantalla reducida a la mitad, para que el ejemplo no sea muy soso. Observe el lector, que los parámetros que hay que pasar a estos métodos es el observador, para que puedan indicarle las dimensiones de la imagen.

El ejemplo siguiente, java1519.java, no proporciona un efecto visual demasiado bueno mientras se carga la imagen para presentarla en pantalla, que se corregirá posteriormente al usar un objetoMediaTracker, para bloquear la tarea hasta que la imagen esté totalmente cargada, con lo que se elimina el parpadeo que se produce al ir presentándose en la pantalla partes de esa imagen que no está totalmente cargada.

El tiempo máximo de carga permitido es un segundo; si la imagen no se carga satisfactoriamente en un segundo, el programa presenta un mensaje indicando la circunstancia y sale. Una imagen grande, o una imagen que se cargue directamente desde Internet (que es una fuente muy lenta), puede necesitar más tiempo. El código completo del ejemplo es el que se reproduce a continuación, y se revisa después.

import java.awt.*;import java.awt.event.*;

// Clase de control del ejemploclass java1519 extends Frame { // Referencia a la imagen Image imagen; public java1519() { this.setTitle( "Tutorial de Java, Gráficos" );

Page 17: codigo imagenes

this.setSize( 275,250 ); // Recogemos en la variable "imagen" el fichero de imagen que // se indica, y que se supone situado en el mismo directorio y // disco que la clase del ejemplo imagen = Toolkit.getDefaultToolkit().getImage( "muneco.gif" ); // Se hace visible el Frame, que en la pantalla da origen a // la ventana, aunque la primera imagen no es visible en el // mismo momento en que aparece la ventana en pantalla, porque // hasta que se invoque por primera vez el método paint(), no // se colocará una imagen en el contendor this.setVisible( true );

// Clase anónima anidada que permite terminar la ejecución del // programa, controlando el botón de cierre del Frame this.addWindowListener( new WindowAdapter() { public void windowClosing( WindowEvent evt ) { // Se sale al sistema System.exit( 0 ); } } ); } public static void main( String[] args ) { // Se instancia un objeto de la clase new java1519(); } // Se sobrecarga el método para pintar la imagen public void paint( Graphics g ) { // Se traslada el origen para evitar el efecto del borde g.translate( this.getInsets().left,this.getInsets().top ); // Se utiliza un objeto MediaTracker para bloquear la tarea hasta // que la imagen esté cargada completamente, antes de intentar // pintarla en pantalla. Si la carga de la imagen falla en el // intervalo de un segundo, el programa se termina. Sin el uso de // un objeto de tipo MediaTracker, si la imagen es pequeña, apenas // se notará el uso de MediaTracker, pero sí que es importante a la // hora de cargar imágenes muy grandes o gran cantidad de ellas MediaTracker tracker = new MediaTracker( this ); // Se añade la imagen a la lista del tracker tracker.addImage( imagen,1 ); try { // Se bloquea la tarea durante un segundo, mientras se // intenta la carga de la imagen. En caso de que la imagen // sea mayor que la que se utiliza en este ejemplo, puede // ser necesario aumentar este tiempo if( !tracker.waitForID(1,1000) ) { System.out.println( "Fallo en la carga de la imagen" ); System.exit( 0 ); } } catch( InterruptedException e ) {

Page 18: codigo imagenes

System.out.println( e ); } // Ahora se pinta la imagen a la mitad de su tamaño normal g.drawImage( imagen,0,0, imagen.getWidth(this)/2,imagen.getHeight(this)/2,this ); // Y se presenta en pantalla this.repaint(); } }

Para revisar el código del ejemplo, se prescindirá de la parte que está duplicada con respecto a ejemplos anteriores, para ver solamente el código nuevo. Para comenzar esta revisión, pues, lo primero en que hay que detenerse es en la sentencia que instancia un objeto de tipo MediaTracker. El parámetro del constructor es el objeto Component a quien pertenecen las imágenes que se van a controlar. En este caso es:

MediaTracker tracker = new MediaTracker( this );

El siguiente código interesante es la sentencia que incorpora una nueva imagen a la lista de imágenes a controlar. En este caso los parámetros a pasar son una referencia al fichero de la imagen y un identificador que se utilizará posteriormente para saber el estado en que se encuentra la carga de la imagen; para este identificador se puede utilizar cualquier valor entero, teniendo en cuenta que este valor representa la prioridad de carga, siendo los valores más bajos los que tienen más alta prioridad.

tracker.addImage( imagen, 1 );

Ahora hay que detenerse en la invocación al método waitForId() sobre el objeto controlador. En este caso, el identificador de la imagen que se está controlando es el parámetro a pasar. También se pasa un valor de 1000 milisegundos como parámetro para hacer que el programa cese el bloqueo y termine si la carga de la imagen no se ha completado dentro de ese intervalo de tiempo. Este método devuelve true si la imagen se ha cargado con éxito y false en cualquier otro caso. Hay algunos otros métodos que se pueden utilizar para este mismo propósito y que devuelven void, por lo que necesitan otras referencias para saber si la imagen se ha cargado bien.

En este caso, el valor de retorno del método waitForID() es utilizado para terminar el programa si la carga de la imagen falla (note el lector el operador "!" delante de la palabra tracker). Este método también lanza un objeto InterruptedException si la carga no se puede completar en el intervalo de tiempo determinado; por ello, la llamada al método debe ir encerrada en un bloque try-catch.

try { // Se bloquea la tarea durante un segundo, mientras se // intenta la carga de la imagen. En caso de que la imagen // sea mayor que la que se utiliza en este ejemplo, puede // ser necesario aumentar este tiempo if( !tracker.waitForID(1,1000) ) { System.out.println( "Fallo en la carga de la imagen" ); System.exit( 0 ); }} catch( InterruptedException e ) { System.out.println( e ); }

Finalmente, se utiliza el método drawImage() para pintar la imagen sobre el contenedor, una vez

Page 19: codigo imagenes

cargada. Cuando el lector ejecute el programa verá que la sensación visual es mucho mejor que la del ejemplo anterior porque solamente se realiza una operación de dibujo para pintar la imagen completa.

// Ahora se pinta la imagen a la mitad de su tamaño normalg.drawImage( imagen,0,0, imagen.getWidth(this)/2,imagen.getHeight(this)/2,this );

Los dos ejemplos que se han visto eran bastante simples, aunque ilustraban una serie de conceptos muy importantes que están involucrados en la manipulación de imágenes. El ejemplo java1520.java, presenta algunos conceptos adicionales. Cuando se ejecuta este ejemplo, aparecen en la ventana cinco imágenes escaladas, para las cuales se ha utilizado el método getScaledImage() de la claseImage a la hora de producir cada una de esas imágenes.

El programa invoca al método getImage() sobre un objeto de la clase Toolkit para asociar la referencia a un objeto Image llamado imagenFuente a un fichero que contiene la imagen en el disco duro. Luego se invoca el método getScaledImage() sobre el objeto imagenFuente sucesivamente cinco veces para generar cinco instancias escaladas de esa imagen, utilizando un algoritmo diferente para la obtención de cada una de ellas.

Se instancia un objeto MediaTracker en el método sobrecargado paint(). El objeto imagenFuente y sus cinco instancias escaladas se añaden a la lista de imágenes que van a ser controladas por el objeto MediaTracker, teniendo todas ellas el mismo identificador, es decir, la misma prioridad.

El método waitForAll() de la clase MediaTacker es el que se utiliza en este caso para bloquear la tarea hasta que se cargan y escalan las imágenes. Durante esta carga, puede haber una pausa de unos 4 o 5 segundos, o incluso mayor, dependiendo de la máquina en que se ejecute el programa y de la complejidad de la imagen; sin embargo, a la hora de su aparición en pantalla, no hay ninguna pausa, las cinco imágenes se presentan en pantalla simultáneamente.

El código completo del ejemplo es el que se presenta a continuación, correspondiendo la imagen anterior a la ejecución del mismo.

import java.awt.*;

Page 20: codigo imagenes

import java.awt.event.*;

class java1520 extends Frame { // Referencias a los objetos de tipo Image que se van a presentar en // pantalla Image imagenFuente,imagen1,imagen2,imagen3,imagen4,imagen5; // Contructor de la clase public java1520() { this.setTitle( "Tutorial de Java, Gráficos" ); this.setSize( 415,260 ); // Se carga la imagen desde el fichero que se indique, que se // supone situado en el directorio actual del disco duro imagenFuente = Toolkit.getDefaultToolkit().getImage( "muneco.gif" ); // Se crean cinco instancias escaladas de la imagen anterior, todas // del mismo tamaño pero utilizando un algoritmo diferente en el // escalado de cada una de ellas, para imagen1 = imagenFuente.getScaledInstance( 136,120, Image.SCALE_AREA_AVERAGING ); imagen2 = imagenFuente.getScaledInstance( 136,120, Image.SCALE_SMOOTH ); imagen3 = imagenFuente.getScaledInstance( 136,120, Image.SCALE_FAST ); imagen4 = imagenFuente.getScaledInstance( 136,120, Image.SCALE_REPLICATE ); imagen5 = imagenFuente.getScaledInstance( 136,120, Image.SCALE_DEFAULT ); // Se hace visible el marco de la ventana. En este momento todavía no // es visible ninguna de las imágenes que se han creado anteriormente this.setVisible(true);

// Clase anidada que permite terminar la ejecución de la animación this.addWindowListener( // Definición de la clase anónima para controlar el cierre de // la ventana new WindowAdapter() { public void windowClosing( WindowEvent evt ) { // Se concluye el programa System.exit( 0 ); } } ); } // Método de control del programa public static void main( String[] args ) { // Se instancia un objeto de esta clase new java1520(); } // Método paint() sobreescrito public void paint( Graphics g ) { // Se traslada el origen para evitar el efecto del borde

Page 21: codigo imagenes

g.translate( this.getInsets().left,this.getInsets().top ); // Se utiliza un objeto MediaTracker para bloquear la tarea hasta // que las imágenes estén cargadas completamente, antes de intentar // pintarlas en pantalla. MediaTracker tracker = new MediaTracker( this ); // Se añaden las imágenes a la lista del tracker tracker.addImage( imagenFuente,1 ); tracker.addImage( imagen1,1 ); tracker.addImage( imagen2,1 ); tracker.addImage( imagen3,1 ); tracker.addImage( imagen4,1 ); tracker.addImage( imagen5,1 ); try{ // Se bloquea la tarea durante el tiempo necesario para la carga // de todas las imágenes tracker.waitForAll(); } catch( InterruptedException e ) { System.out.println( e ); } // Comprobamos si ha habido algún error en la carga de las imágenes // y su manipulación. Si todo ha ido bien, las cinco imágenes // aparecerán en pantalla simultáneamente if( !tracker.isErrorAny() ) { g.drawImage( imagen1,0,0,this ); g.drawImage( imagen2,137,0,this ); g.drawImage( imagen3,275,0,this ); g.drawImage( imagen4,70,120,this ); g.drawImage( imagen5,210,120,this ); } else { System.out.println( "Error en la carga de imágenes" ); System.exit( 1 ); } } }

A la hora de que las imágenes aparezcan en pantalla, no hay pausas aparentes entre una y otra. Si se utiliza el método drawImage() para presentar y escalar la imagen al mismo tiempo, cosa que el lector puede realizar como ejercicio, se observan notables retrasos entre la presentación de una imagen y la siguiente.

En este caso, el primer código en que merece detenerse es donde se utiliza el método getImage()para asociar la variable de referencia imagenFuente con un fichero de imagen del disco duro. Esencialmente es lo mismo que en ejemplos anteriores; sin embargo, esa variable de referencia se utiliza para crear cinco instancias adicionales escaladas utilizando el método getScaledInstance(), en cada una de cuales se utiliza un algoritmo de escalado distinto, especificado en el último parámetro de la llamada al método.

La anchura y altura de la imagen escalada se indican en los dos primeros parámetros. Si alguno de ellos es -1, el tamaño escalado en pixeles de esa dimensión de la imagen estará basado en el otro parámetro para mantener la misma relación de aspecto que la imagen original. Si los dos parámetros

Page 22: codigo imagenes

son positivos, la imagen se escalará a las dimensiones que se indiquen en ellos.

En ese caso, como muestran las líneas de código siguientes, se pasa -1 como segundo parámetro en las cinco invocaciones al método getScaledImage(), escalándolas además al mismo tamaño, utilizando algoritmos diferentes.

// Se carga la imagen desde el fichero que se indique, que se // supone situado en el directorio actual del disco duroimagenFuente = Toolkit.getDefaultToolkit().getImage( "muneco.gif" ); // Se crean cinco instancias escaladas de la imagen anterior, todas// del mismo tamaño pero utilizando un algoritmo diferente en el// escalado de cada una de ellas, para imagen1 = imagenFuente.getScaledInstance( 136,120, Image.SCALE_AREA_AVERAGING );imagen2 = imagenFuente.getScaledInstance( 136,120, Image.SCALE_SMOOTH );imagen3 = imagenFuente.getScaledInstance( 136,120, Image.SCALE_FAST );imagen4 = imagenFuente.getScaledInstance( 136,120, Image.SCALE_REPLICATE );imagen5 = imagenFuente.getScaledInstance( 136,120, Image.SCALE_DEFAULT );

El siguiente trozo interesante del ejemplo es el trozo del método paint() donde se incorporan las seis referencias de tipo Image a la lista de imágenes que controlará el objeto MediaTracker. Las seis se añaden con el mismo nivel de prioridad.

// Se utiliza un objeto MediaTracker para bloquear la tarea hasta// que las imágenes estén cargadas completamente, antes de intentar// pintarlas en pantalla. MediaTracker tracker = new MediaTracker( this );// Se añaden las imágenes a la lista del trackertracker.addImage( imagenFuente,1 );tracker.addImage( imagen1,1 );tracker.addImage( imagen2,1 );tracker.addImage( imagen3,1 );tracker.addImage( imagen4,1 );tracker.addImage( imagen5,1 );

Las líneas siguientes muestran el bloque try-catch que bloquea la tarea hasta que las seis imágenes estén completamente cargadas y escaladas.

try{ // Se bloquea la tarea durante el tiempo necesario para la carga // de todas las imágenes tracker.waitForAll();} catch( InterruptedException e ) { System.out.println( e ); }

El último trozo de código más interesante es el que utiliza una versión sin escalado del métododrawImage() para pintar las cinco imágenes escaladas previamente. Esto es importante,

Page 23: codigo imagenes

porque al no necesitar las imágenes ningún procesado, su aparición en pantalla se realiza en un abrir y cerrar de ojos. En este caso también se ha utilizado el método isErrorAny() (con el operador not) para confirmar que no se han producido errores al invocar al método drawImage() para pintar las imágenes en pantalla.

// Comprobamos si ha habido algún error en la carga de las imágenes// y su manipulación. Si todo ha ido bien, las cinco imágenes // aparecerán en pantalla simultáneamenteif( !tracker.isErrorAny() ) { g.drawImage( imagen1,0,0,this ); g.drawImage( imagen2,137,0,this ); g.drawImage( imagen3,275,0,this ); g.drawImage( imagen4,70,120,this ); g.drawImage( imagen5,210,120,this ); }else { System.out.println( "Error en la carga de imágenes" ); System.exit( 1 ); }

Se puede hablar de dos tipos:

~- Animacion de tipo ‘dibujos animados’: presentar una sucesion rapida de imagenes precargadas GIF o JPEG (p205 21days)

~- Animacion dibujada en tiempo real; veamos~- Un ejemplo que NO funciona (no aparece nada en pantalla);

// MalRelojDigitalApplet.java   import java.awt.Graphics; import java.awt.Font; import java.util.Date;   public class MalRelojDigitalApplet extends java.applet.Applet { Font fuente = new Font("TimesRoman", Font.BOLD, 24); Date fecha;   public void start() { while (true) { fecha = new Date(); repaint(); 

Page 24: codigo imagenes

try { Thread.sleep(10000); } catch(InterruptedException e) {;} } }   public void paint(Graphics g) { g.setFont(fuente); g.drawString(fecha.toString(), 10, 50); } }  

~- ¿Porqué no funciona?: paint nunca se ejecuta ya que el bucle while monopoliza la ejecucion.~- Cómo debe hacerse para que funcione: usando Threads

Ej:// BienRelojDigitalApplet.java   import java.awt.Graphics; import java.awt.Font; import java.util.Date;   public class BienRelojDigitalApplet extends java.applet.Applet implements Runnable //cambio { Font fuente = new Font("TimesRoman", Font.BOLD, 24); Date fecha; Thread runner; //cambio   //cambio public void start() 

{ if (runner == null) { runner = new Thread(this); runner.start(); } else { System.out.println("Stop"); } 

Page 25: codigo imagenes

}   //cambio public void stop() { if (runner != null) { runner.stop(); runner = null; } else { System.out.println("Stop"); } }   //cambio public void run() { while (true) { fecha = new Date(); repaint(); try { Thread.sleep(1000); } catch(InterruptedException e) {;} } }   public void paint(Graphics g) { { g.setFont(fuente); g.drawString(fecha.toString(), 10, 50); } } }  

~- El problema del parpadeo: el programa anterior funciona, pero se aprecia un parpadeo molesto cada 3 o 4 segundos~- Porqué aparece parpadeo:~~- (las imagenes son demasiado grandes. Con pequeños cuadros es inapreciable el parpadeo)~~- repaint() llama al metodo update()~~- update() borra la pantalla por completo y la rellena con el color de fondo. Despues

Page 26: codigo imagenes

llama a paint()

public void update(Graphics g) { g.setColor(getBackground()); g.fillRect(0,0,width,height); g.setColor(getForeground()); paint(g); }

~~- paint() escribe el nuevo pantallazo~- Es la llamada a update() la que produce el parpadeo.~- Soluciones posibles

~~1) Sobreescribir update() para que no borre la pantalla o que solo borre una cierta area~~1) Clipping: re-dibujar solo aquello que cambia. Metodo clipRect() de la clase Graphics~~1) Doble-buffering: hacer los cambios en una pantalla ‘virtual’ y volcar el resultado a la pantalla ‘real’ (p212 21days). Es el metodo mas complicado...

Visor de Imágenes

Visor de ImágenesEste Applet es un pequeño visualizador de imágenes, lo que hace es mostrarnos en Miniatura unas imágenes, en la parte inferior y al momento de seleccionar una esta se visualiza en grande en la parte superior del Applet.

Imágenes

Page 28: codigo imagenes

package Clases;

import java.awt.BorderLayout;import java.awt.event.*;import javax.swing.*;

public class Principal extends JApplet { panelVisorMiniaturas pm; panelVisualizador pv; public void init(){ pm=new panelVisorMiniaturas(this); pv=new panelVisualizador(this); add(pv); ImageIcon imgbotons=new ImageIcon(this.getClass().getResource("../Imagenes/siguiente.png")); JButton btnsiguiente=new JButton(imgbotons); btnsiguiente.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { pm.siguienteImagen(); pm.quitarBorder(); } }); ImageIcon imgbotona=new ImageIcon(this.getClass().getResource("../Imagenes/anterior.png")); JButton btnanterior=new JButton(imgbotona); btnanterior.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { pm.anteriorImagen(); pm.quitarBorder(); } }); JPanel pbotones=new JPanel(); pbotones.add(btnanterior); pbotones.add(pm); pbotones.add(btnsiguiente); add(pbotones,BorderLayout.SOUTH); }

public panelVisorMiniaturas getPm() { return pm; }

public void setPm(panelVisorMiniaturas pm) { this.pm = pm; }

Page 29: codigo imagenes

public panelVisualizador getPv() { return pv; }

public void setPv(panelVisualizador pv) { this.pv = pv; }}

Clase panelVisualizador

package Clases;

import java.awt.Graphics;

import javax.swing.ImageIcon;import javax.swing.JPanel;

public class panelVisualizador extends JPanel { Principal p; ImageIcon imagen; public panelVisualizador(Principal prin){ p=prin; } public void paintComponent(Graphics g){ super.paintComponents(g); if(imagen!=null){ g.drawImage(imagen.getImage(),0 , 0, p.getPv().getWidth(), p.getPv().getHeight(), this); } } public Principal getP() { return p; }

public void setP(Principal p) { this.p = p; }

public ImageIcon getImagen() { return imagen; }

public void setImagen(ImageIcon imagen) { this.imagen = imagen; }}

Page 30: codigo imagenes

Clase panelVisorMiniaturaspackage Clases;

import java.awt.BorderLayout;import java.awt.Dimension;import java.awt.GridLayout;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.MouseEvent;import java.awt.event.MouseListener;

import javax.swing.ImageIcon;import javax.swing.JButton;import javax.swing.JPanel;

public class panelVisorMiniaturas extends JPanel { panelMiniatura paneles[]=new panelMiniatura[4]; ImageIcon imagenes[]=new ImageIcon[13]; int indices[]=new int[4]; Principal p; public panelVisorMiniaturas(Principal prin){ cargarImagenes(); p=prin; setLayout(new GridLayout(1, 4,10,10)); for(int i=0;i<4;i++){ paneles[i]=new panelMiniatura(imagenes[i],p); add(paneles[i]); } } public void siguienteImagen(){ if(indices[3]<12){ for(int i=0;i<4;i++){ indices[i]=indices[i]+1; paneles[i].setImagen(imagenes[indices[i]]); } repaint(); p.repaint(); } } public void anteriorImagen(){ if(indices[0]>0){ for(int i=0;i<4;i++){ indices[i]=indices[i]-1; paneles[i].setImagen(imagenes[indices[i]]); } repaint();

Page 31: codigo imagenes

p.repaint(); } } public void cargarImagenes(){ for(int i=0;i<4;i++){ indices[i]=i; } for(int i=0;i<13;i++){ imagenes[i]=new ImageIcon(this.getClass().getResource("../Imagenes/"+(i+1)+".jpg")); } } public void quitarBorder(){ for(int i=0;i<4;i++){ paneles[i].setBorder(null); } }

public panelMiniatura[] getPaneles() { return paneles; }

public void setPaneles(panelMiniatura[] paneles) { this.paneles = paneles; }

public ImageIcon[] getImagenes() { return imagenes; }

public void setImagenes(ImageIcon[] imagenes) { this.imagenes = imagenes; }

public int[] getIndices() { return indices; }

public void setIndices(int[] indices) { this.indices = indices; }}

Clase panelMiniaturapackage Clases;

import java.awt.*;import java.awt.event.MouseEvent;import java.awt.event.MouseListener;import javax.swing.*;

Page 32: codigo imagenes

public class panelMiniatura extends JPanel { ImageIcon imagen; Principal p; public panelMiniatura(ImageIcon img,Principal prin){ imagen=img; p=prin; setPreferredSize(new Dimension(80,80)); MouseListener ml=new MouseListener() { public void mouseReleased(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseExited(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseClicked(MouseEvent e) { p.getPm().quitarBorder(); panelMiniatura pm=(panelMiniatura)e.getSource(); pm.setBorder(BorderFactory.createLineBorder(Color.ORANGE, 3)); p.getPv().setImagen(pm.getImagen()); p.getPv().repaint(); p.repaint(); } }; addMouseListener(ml); } public void paintComponent(Graphics g){ super.paintComponents(g); if(imagen!=null){ g.drawImage(imagen.getImage(), 0, 0, 80, 80, this); } }

public ImageIcon getImagen() { return imagen; }

public void setImagen(ImageIcon imagen) { this.imagen = imagen; }}

Page 33: codigo imagenes

El código es claro, tengo en la clase principal un objeto panelVisualizador y un objeto panelVisorMiniaturas, ademas aquí coloco los botones de siguiente y anterior. Dentro de panelVisorMiniaturas estan unos objetos panelMiniatura la cual cada una tiene su imagen y la escale para que quedara 80 x 80 pixeles. Ademas a cada panelMiniatura le coloque un MouseListener para que cuando seleccionen un panelMiniatura este le mande la imagen a panelVisualizador y lo muestre en grande.

Applet

Este es un ejemplo de un applet, Animacion.java, que crea un thread de animación que nos presenta el globo terráqueo en rotación. Aquí podemos ver que estamos creando un thread de sí mismo, concurrencia. Además, animacion.start() llama al start() del thread, no del applet, que automáticamente llamará a run():

import java.awt.*;import java.applet.Applet;

public class Animacion extends Applet implements Runnable { Image imagenes[]; MediaTracker tracker; int indice = 0; Thread animacion;

int maxAncho,maxAlto; Image offScrImage; // Componente off-screen para doble buffering Graphics offScrGC;

// Nos indicará si ya se puede pintar boolean cargado = false;

// Inicializamos el applet, establecemos su tamaño y // cargamos las imágenes public void init() { // Establecemos el supervisor de imágenes tracker = new MediaTracker( this ); // Fijamos el tamaño del applet maxAncho = 100; maxAlto = 100; imagenes = new Image[36];

Page 34: codigo imagenes

// Establecemos el doble buffer y dimensionamos el applet try { offScrImage = createImage( maxAncho,maxAlto ); offScrGC = offScrImage.getGraphics(); offScrGC.setColor( Color.lightGray ); offScrGC.fillRect( 0,0,maxAncho,maxAlto ); resize( maxAncho,maxAlto ); } catch( Exception e ) { e.printStackTrace(); }

// Cargamos las imágenes en un array for( int i=0; i < 36; i++ ) { String fichero = new String( "Tierra"+String.valueOf(i+1)+".gif" ); imagenes[i] = getImage( getDocumentBase(),fichero ); // Registramos las imágenes con el tracker tracker.addImage( imagenes[i],i ); }

try { // Utilizamos el tracker para comprobar que todas las // imágenes están cargadas tracker.waitForAll(); } catch( InterruptedException e ) { ; } cargado = true; }

// Pintamos el fotograma que corresponda public void paint( Graphics g ) { if( cargado ) g.drawImage( offScrImage,0,0,this ); }

// Arrancamos y establecemos la primera imagen public void start() { if( tracker.checkID( indice ) ) offScrGC.drawImage( imagenes[indice],0,0,this ); animacion = new Thread( this ); animacion.start(); } // Aquí hacemos el trabajo de animación // Muestra una imagen, para, muestra la siguiente... public void run() { // Obtiene el identificador del thread Thread thActual = Thread.currentThread();

// Nos aseguramos de que se ejecuta cuando estamos en un // thread y además es el actual while( animacion != null && animacion == thActual ) { if( tracker.checkID( indice ) )

Page 35: codigo imagenes

{ // Obtenemos la siguiente imagen offScrGC.drawImage( imagenes[indice],0,0,this ); indice++; // Volvemos al principio y seguimos, para el bucle if( indice >= imagenes.length ) indice = 0; }

// Ralentizamos la animación para que parezca normal try { animacion.sleep( 200 ); } catch( InterruptedException e ) { ; } // Pintamos el siguiente fotograma repaint(); } } }

En el ejemplo podemos observar más cosas. La variable thActual es propia de cada thread que se lance, y la variable animacion la estarán viendo todos los threads. No hay duplicidad de procesos, sino que todos comparten las mismas variables; cada thread, sin embargo, tiene su pila local de variables, que no comparte con nadie y que son las que están declaradas dentro de las llaves del método run().

La excepción InterruptedExcepcion salta en el caso en que se haya tenido al thread parado más tiempo del debido. Es imprescindible recoger esta excepción cuando se están implementando threads, tanto es así, que en el caso de no recogerla, el compilador generará un error.