La siguiente clase (Java 1.4+) permite obtener todos los fotogramas de una imagen múltiple (por ejemplo, un GIF animado) y operar sobre ellas de forma independiente. La clase no gestiona la animación en sí, ni tampoco lee la metainformación contenida en el archivo (con datos por ejemplo acerca de las repeticiones o de los tiempos de visuzalización), pero es un buen punto de partida si se quiere avanzar en las profundidades del sistema ImageIO. La clase además es compatible con los plugins de procesamiento de imágenes que haya instalados en el sistema, de forma que no está necesariamente limitada a imágenes GIF. Licencia LGPL .
Esto es lo que hace el programa:
En la línea 37, obtenemos la instancia predeterminada de IIORegistry - el sitio donde los diferentes plugins de procesamiento de imágenes se dan de alta en Java. A continuación (línea 38), solicitamos al registro una lista de todos los proveedores que son capaces de construirnos un ImageInputStream . Para cada uno de estos proveedores, les preguntamos si el tipo de objetos de entrada de los que son capaces de leer información coinciden con el tipo de flijo que nosotros tenemos (un InputStream). Si ese es el caso, entonces cogemos el primero que sea capaz de hacerlo. ¿Por qué el sistema no puede utilizar InputStream directamente y necesita en cambio ImageInputStream? Porque los procesadores de imágenes necesitan un flujo un poco más flexible y versátil que InputStream, que tiene entre otras capacidades la de acceso aleatorio y lectura de bits individuales.
Si para la línea 49 no hemos encontrado ningún proveedor capaz de crear ImageInputStreams para el tipo de flujo que tenemos, abortamos la lectura con un error.
El siguiente paso es ver qué decodificadores son capaces de leer la información contenida en el flujo. El método fue diseñado para trabajar con flujos y no con archivos no solo por motivos de flexibilidad, sino también porque confiar en la extensión de un archivo como guía sobre lo que contiene es peligroso y con múltiples posibilidades de fallo.
De esta forma, en la línea 52 solicitamos al registro que nos dé una lista de todo los ImageReaderSpis que son capaces de decodificar la información del flujo. El filtrado lo realizamos nosotros mismos mediante la clase estática CanDecodeFilter class (líneas 73 a 90). Esta clase es un filtro de retrollamada al que el método getServiceProviders() llama por cada proveedor ImageReaderSpi registrado. El filtro almacena la posición actual del flujo (línea 82) y pregunta al proveedor si puede decodificar la información del flujo. Posteriormente se recupera la posición del flujo (línea 84)
Utilizando este filtro, en la línea 52 tenemos un iterador que es capaz de recorrer todos los proveedores compatibles. Si no hay ninguno (lineas 54-55), lanzamos una excepción. En caso de que sí los haya, simplemente elegimos el primero de ellos y le pedimos que nos cree un lector (ImageReader), inicializamos el lector con el conjunto de parámetros que sea predeterminado para su tipo y establecemos su origen de datos (lines 58-61). Desde este punto en adelante es fácil : simplemente pedimos al lector que nos diga el número de fotogramas (el parámetrro "true" indica al método getNumImages() que puede escanear todo el flujo si es necesario con el fín de obtener la información), y a continuación procedemos a leer uno a uno todos los fotogramas con el método read().
0001/** 0002 * FrameReader.java - Retrieves all frames from an image 0003 * Copyright (c) 2007 Alexander Hristov . 0004 * 0005 * Licensed under the LGPL License - http://www.gnu.org/licenses/lgpl.txt 0006 * 0007 * This library is free software; you can redistribute it and/or 0008 * modify it under the terms of the GNU Lesser General Public 0009 * License as published by the Free Software Foundation; either 0010 * version 2.1 of the License, or (at your option) any later version. 0011 * 0012 * This library is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 * Lesser General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU Lesser General Public 0018 * License along with this library; if not, write to the Free Software 0019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0020 */ 0021 0022import java.awt.image.BufferedImage; 0023import java.io.IOException; 0024import java.io.InputStream; 0025import java.util.Iterator; 0026 0027import javax.imageio.ImageReadParam; 0028import javax.imageio.ImageReader; 0029import javax.imageio.spi.IIORegistry; 0030import javax.imageio.spi.ImageInputStreamSpi; 0031import javax.imageio.spi.ImageReaderSpi; 0032import javax.imageio.spi.ServiceRegistry; 0033import javax.imageio.stream.ImageInputStream; 0034 0035public class FrameReader { 0036 public static BufferedImage[] getAllFrames(InputStream in) throws IOException { 0037 IIORegistry providers = IIORegistry.getDefaultInstance(); 0038 Iterator it = providers.getServiceProviders(ImageInputStreamSpi.class,true); 0039 ImageInputStream imgStream = null; 0040 0041 // Create an ImageInputStream with the first appropriate SPI 0042 while (it.hasNext()) { 0043 ImageInputStreamSpi spi = (ImageInputStreamSpi)it.next(); 0044 if (spi.getInputClass().isInstance(in)) { 0045 imgStream = spi.createInputStreamInstance(in); 0046 break; 0047 } 0048 } 0049 if (imgStream == null) 0050 throw new IOException("No appropriate SPI for this input stream "); 0051 0052 it = providers.getServiceProviders( 0053 ImageReaderSpi.class, new CanDecodeFilter(imgStream),true); 0054 if (!it.hasNext()) 0055 throw new IOException("No class can read this image format"); 0056 0057 ImageReaderSpi readerSpi = (ImageReaderSpi)it.next(); 0058 ImageReader reader = readerSpi.createReaderInstance(); 0059 ImageReadParam param = reader.getDefaultReadParam(); 0060 0061 reader.setInput(imgStream, false, true); 0062 int numFrames = reader.getNumImages(true); 0063 BufferedImage[] frames = new BufferedImage[numFrames]; 0064 for (int i = 0; i < numFrames; i++) { 0065 frames[i] = reader.read(i,param); 0066 } 0067 0068 imgStream.close(); 0069 reader.dispose(); 0070 return frames; 0071 } 0072 0073 static class CanDecodeFilter implements ServiceRegistry.Filter { 0074 ImageInputStream stream; 0075 public CanDecodeFilter(ImageInputStream stream) { 0076 this.stream = stream; 0077 } 0078 public boolean filter(Object element) { 0079 try { 0080 ImageReaderSpi spi = (ImageReaderSpi)element; 0081 boolean canDecode = false; 0082 stream.mark(); 0083 canDecode = spi.canDecodeInput(stream); 0084 stream.reset(); 0085 return canDecode; 0086 } catch (IOException e) { 0087 return false; 0088 } 0089 } 0090 } 0091} 0092
|
FrameReader.java ( 3 Kb ) |