The following class (Java 1.4+) allows you to retrieve all frames from a multiframe image (usually an animated GIF) and operate on them independantly. The class does not handle animation, nor metadata (like looping times), but it is a good starting point if you want to delve into the advanced features of the ImageIO system. It also works with whatever image plugins are installed, so it is not necessarily limited to animated GIFs. LGPL License
Here is what the program does:
In lines 37, we retrieve the default instance of IIORegistry - the place where the different image-handling plugins register themselves. After that (line 38), we request from the registry a list of all providers that can build an ImageInputStream for us. For each of these providers, we ask him if the input type of objects he requires is compatible with the input type we have (an InputStream). If so, then we just pick the first provider that answers positively. Why can't we just use the InputStream? Because image processing plugins require a more versatile stream that is - for example - capable of seeking forward and backward, reading individual bits, etc.
If by line 49 we haven't found any provider capable of creating ImageInputStreams from the InputStream we have, we have no choice but to abort the operation.
The next step is checking which decoder class can handle the information present in the InputStream. I specifically designed the method to handle streams and not just file names not only because it is more flexible, but because relying on extensions for determining the contents of a file is dangerous and error-prone.
So in line 52 we ask the registry to give us a list of all ImageReaderSpis that can decode the information in the stream. The "Can decode" filter is implemented in the static CanDecodeFilter class (lines 73 to 90). It is a callback filter that is called by the getServiceProviders() method for each service provider. In this case, each call supplies a registered ImageReaderSpi. The filter stores the current position of the image stream (line 82), asks the ImageReaderSpi if it can read the stream and afterwards restores the stream position (line 84), just in case.
So using this filter on line 52 we have an iterator that can iterate all capable readers. If there are none (line 54-55), we throw an exception. Otherwise we just pick the first one, tell him to create a reader, initialize the reader with the default parameters for this kind of reader and set its input to our image stream (lines 58-61). From this point its easy : we just ask the reader about the number of frames contained in the image (the true parameter tells that the reader can read the whole stream if necessary in order to get this information), and then proceed to read each of the frames using the read() method.
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 ) |