summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSven de Marothy <sven@physto.se>2006-06-26 16:06:30 +0000
committerSven de Marothy <sven@physto.se>2006-06-26 16:06:30 +0000
commitcb6f2dd309554aa8a3cb1a999d770dafbe72ab5a (patch)
treed4621c0870bf020f492c0d48193be051ee555049
parenta91acd07fa7a7b31a322b644b6d3270ec46b89c2 (diff)
downloadclasspath-cb6f2dd309554aa8a3cb1a999d770dafbe72ab5a.tar.gz
2006-06-26 Sven de Marothy <sven@physto.se>
* gnu/javax/imageio/gif/GIFFile.java * gnu/javax/imageio/gif/GIFImageReader.java * gnu/javax/imageio/gif/GIFImageSpi.java * gnu/javax/imageio/gif/GIFStream.java New files. * javax/imageio/spi/IIORegistry.java: Load new GIF decoder plugin.
-rw-r--r--gnu/javax/imageio/gif/GIFFile.java709
-rw-r--r--gnu/javax/imageio/gif/GIFImageReader.java242
-rw-r--r--gnu/javax/imageio/gif/GIFImageReaderSpi.java122
-rw-r--r--gnu/javax/imageio/gif/GIFStream.java102
-rw-r--r--javax/imageio/spi/IIORegistry.java3
5 files changed, 1177 insertions, 1 deletions
diff --git a/gnu/javax/imageio/gif/GIFFile.java b/gnu/javax/imageio/gif/GIFFile.java
new file mode 100644
index 000000000..a11a8c0c6
--- /dev/null
+++ b/gnu/javax/imageio/gif/GIFFile.java
@@ -0,0 +1,709 @@
+/* GIFFile.java -- GIF decoder
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.javax.imageio.gif;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+
+/**
+ * GIFFile - reads a GIF file.
+ *
+ * This class only does the bare minimum work, and returns the data in raw
+ * formats (described below). The class is J2ME compatible, and hopefully
+ * we can keep it that way without any significant overhead.
+ *
+ * @author Sven de Marothy.
+ */
+public class GIFFile
+{
+ // "NETSCAPE2.0" - identifier
+ private final static byte[] nsBlock = new byte[]
+ {0x4e, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2e, 0x30 };
+
+ /**
+ * Block identifiers
+ */
+ private final static int EXTENSION = 0x21;
+ private final static int LOCAL = 0x2C;
+ private final static int TERMINATOR = 0x3B;
+
+ /**
+ * Extension block types
+ */
+ private final static int EXTENSION_COMMENT = 254;
+ private final static int EXTENSION_GCONTROL = 249;
+ private final static int EXTENSION_APPLICATION = 255;
+
+ /**
+ * Undraw commands for animation.
+ */
+ private final static int UNDRAW_OVERWRITE = 1;
+ private final static int UNDRAW_RESTORE_BACKGROUND = 2;
+ private final static int UNDRAW_RESTORE_PREVIOUS = 3;
+
+ /**
+ * Image position and dimensions (images may be partial)
+ */
+ private int x, y, width, height;
+
+ /**
+ * Global dimensions
+ */
+ private int globalWidth, globalHeight;
+
+ /**
+ * Background color index.
+ */
+ private byte bgIndex;
+
+ /**
+ * Number of colors
+ */
+ private int nColors;
+
+ /**
+ * Global palette, if any
+ */
+ private byte[] globalPalette;
+
+ /**
+ * Any
+ */
+ private boolean hasGlobalColorMap;
+
+ /**
+ * Local palette, if any (used if available)
+ */
+ private byte[] localPalette;
+
+ /**
+ * Interlaced GIF or not?
+ */
+ private boolean interlaced;
+
+ /**
+ * Has transparency?
+ */
+ private boolean hasTransparency;
+
+ /**
+ * Undraw mode (animations)
+ */
+ private int undraw;
+
+ /**
+ * Transparent index;
+ */
+ private int transparentIndex;
+
+ /**
+ * The uncompressed raster
+ */
+ private byte[] raster;
+
+ /**
+ * The compressed data (freed after uncompressing)
+ */
+ private byte[] compressedData;
+
+ /**
+ * Frame delay in 100ths of a second ( centiseconds, metrically )
+ */
+ private int duration;
+
+ /**
+ * Indices used during decompression
+ */
+ private int dataBlockIndex;
+
+ /**
+ * The file comment , if a comment block exists.
+ */
+ private String comment;
+
+ /**
+ * Fields used by getBits()
+ */
+ private int remainingBits = 0;
+ private int currentBits = 0;
+
+ /**
+ * Netscape animation extension
+ */
+ private boolean isLooped = false;
+
+ /** Number of loops, 0 = infinite */
+ private int loops;
+
+ /**
+ * Additional frames if it's an animated GIF.
+ */
+ private Vector animationFrames;
+
+ /**
+ * Loads the file from an input stream, which is not closed.
+ * @throws IOException if an I/O error occured.
+ * @throws GIFException if some file parsing error occured
+ */
+ public GIFFile(InputStream in) throws IOException, GIFException
+ {
+ // Validate the signature
+ if( !readSignature( in ) )
+ throw new GIFException("Invalid GIF signature.");
+
+ {
+ byte[] data = new byte[7];
+ if (in.read(data) != 7)
+ throw new IOException("Couldn't read global descriptor.");
+
+ globalWidth = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
+ globalHeight = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
+ byte flags = data[4];
+ bgIndex = data[5];
+ nColors = (1 << (( flags & 0x07) + 1));
+ hasGlobalColorMap = ((flags & 0x80) != 0);
+ }
+
+ if( hasGlobalColorMap )
+ {
+ globalPalette = new byte[ nColors * 3 ];
+ if( in.read( globalPalette ) != nColors * 3 )
+ throw new IOException("Couldn't read color map.");
+ }
+
+ int c = in.read();
+ while( c == EXTENSION )
+ {
+ readExtension( in );
+ c = in.read();
+ }
+
+ if( c != LOCAL )
+ throw new GIFException("Extension blocks not followed by a local descriptor ("+c+")");
+
+ loadImage( in );
+ c = in.read();
+
+ if( c == TERMINATOR ) // Not an animated GIF.
+ return;
+
+ // Load animation frames. Just quit if an error occurs instead
+ // of throwing an exception.
+ animationFrames = new Vector();
+ try
+ {
+ while( c != TERMINATOR )
+ {
+ animationFrames.add( new GIFFile( this, in, c ) );
+ c = in.read();
+ }
+ }
+ catch(IOException ioe)
+ {
+ }
+ catch(GIFException gife)
+ {
+ }
+ }
+
+ /**
+ * Constructor for additional animation frames.
+ */
+ private GIFFile(GIFFile parent, InputStream in, int c)
+ throws IOException, GIFException
+ {
+ // Copy global properties.
+ globalWidth = parent.globalWidth;
+ globalHeight = parent.globalHeight;
+ nColors = parent.nColors;
+ globalPalette = parent.globalPalette;
+ hasGlobalColorMap = parent.hasGlobalColorMap;
+ interlaced = parent.interlaced;
+ comment = parent.comment;
+ isLooped = parent.isLooped;
+ loops = parent.loops;
+
+ while( c == EXTENSION )
+ {
+ readExtension( in );
+ c = in.read();
+ }
+
+ if( c != LOCAL )
+ throw new GIFException("Extension blocks not followed by a local descriptor ("+c+")");
+
+ loadImage( in );
+ }
+
+ /**
+ * Reads a GIF file signature from an inputstream and checks it.
+ *
+ * @param in - the stream (reads 6 bytes, does not close or reset).
+ * @return true if the signature is a valid GIF signature.
+ * @throws an IOException if the signature could not be read.
+ */
+ public static boolean readSignature( InputStream in ) throws IOException
+ {
+ byte[] data = new byte[6];
+ if (in.read(data) != 6)
+ throw new IOException("Couldn't read signature.");
+
+ if( data[0] != 0x47 || data[1] != 0x49 || data[2] != 0x46 ||
+ data[3] != 0x38 ) // GIF8
+ return false;
+
+ if( (data[4] != 0x39 && data[4] != 0x37) || // 7 | 9
+ (data[5] != 0x61 && data[5] != 0x62) ) // 'a' or 'b'
+ return false;
+ return true;
+ }
+
+
+ /**
+ * Loads the image local descriptor and then loads/decodes the image raster,
+ * and then performs any necessary postprocessing like deinterlacing.
+ */
+ private void loadImage(InputStream in)
+ throws IOException, GIFException
+ {
+ readLocal( in );
+
+ try
+ {
+ decodeRaster( in );
+ }
+ catch(ArrayIndexOutOfBoundsException aioobe)
+ {
+ throw new GIFException("Error decompressing image.");
+ }
+
+ if( interlaced ) // Clean up
+ deinterlace();
+ packPixels();
+ }
+
+ /**
+ * Pack the pixels if it's a 2, 4 or 16 color image.
+ * While GIF may support any number of colors from 2-256, we won't bother
+ * trying to pack pixels not resulting in even byte boundaries.
+ * (AWT doesn't support that anyway, and most apps do the same.)
+ */
+ private void packPixels()
+ {
+ if( nColors != 2 && nColors != 4 && nColors != 16 )
+ return;
+
+ int nbits = 1;
+ int ppbyte = 8;
+ if( nColors == 4 )
+ {
+ nbits = 2;
+ ppbyte = 4;
+ }
+ else if( nColors == 16 )
+ {
+ nbits = 4;
+ ppbyte = 2;
+ }
+
+ int rem = (width & (ppbyte - 1));
+ int w = ( rem == 0 ) ? (width / ppbyte) :
+ ((width + ppbyte - rem) / ppbyte);
+ byte[] nr = new byte[ w * height ];
+ for(int j = 0; j < height; j++)
+ {
+ for(int i = 0; i < width - ppbyte; i += ppbyte)
+ for(int k = 0; k < ppbyte; k++)
+ nr[ j * w + (i / ppbyte) ] |= (byte)((raster[ width * j + i + k ]
+ << (8 - nbits * (1 + k))));
+ for(int i = 0; i < rem; i++)
+ nr[ j * w + w - 1 ] |= (byte)((raster[ width * j + width - rem + i ]
+ << (nbits * (rem - i))));
+ }
+ raster = nr;
+ }
+
+ /**
+ * Returns the (global) width
+ */
+ public int getWidth()
+ {
+ return width;
+ }
+
+ /**
+ * Returns the image height
+ */
+ public int getHeight()
+ {
+ return height;
+ }
+
+ /**
+ * Returns the # of colors.
+ */
+ public int getNColors()
+ {
+ return nColors;
+ }
+
+ /**
+ * Returns whether the GIF has transparency.
+ */
+ public boolean hasTransparency()
+ {
+ return hasTransparency;
+ }
+
+ /**
+ * Returns the index of the transparent color.
+ */
+ public int getTransparentIndex()
+ {
+ return transparentIndex;
+ }
+
+ /**
+ * Retuns the GIF file comment, or null if none exists.
+ */
+ public String getComment()
+ {
+ return comment;
+ }
+
+ /**
+ * Get duration of the frame for animations.
+ */
+ public int getDuration()
+ {
+ return duration;
+ }
+
+ /**
+ * Deinterlaces the image.
+ */
+ private void deinterlace()
+ {
+ byte[] nr = new byte[ width * height ];
+ int n = 0;
+ for(int i = 0; i < ((height + 7) >> 3); i++)
+ {
+ System.arraycopy( raster, n, nr, width * i * 8, width );
+ n += width;
+ }
+ for(int i = 0; i < ((height + 3) >> 3); i++)
+ {
+ System.arraycopy( raster, n, nr, width * ( 8 * i + 4 ), width );
+ n += width;
+ }
+ for(int i = 0; i < (height >> 2); i++)
+ {
+ System.arraycopy( raster, n, nr, width * (4 * i + 2), width );
+ n += width;
+ }
+ for(int i = 0; i < (height >> 1); i++)
+ {
+ System.arraycopy( raster, n, nr, width * (2 * i + 1), width );
+ n += width;
+ }
+ raster = nr;
+ }
+
+ /**
+ * Reads the local descriptor
+ */
+ private void readLocal(InputStream in) throws IOException
+ {
+ byte[] data = new byte[9];
+ if (in.read(data) != 9)
+ throw new IOException("Couldn't read local descriptor.");
+ x = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
+ y = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
+ width = ((data[5] & 0xFF) << 8) | (data[4] & 0xFF);
+ height = ((data[7] & 0xFF) << 8) | (data[6] & 0xFF);
+ byte flags = data[8];
+ interlaced = (( flags & 0x40 ) != 0);
+ if( (flags & 0x80) != 0 )
+ { // has a local color map
+ int nLocalColors = (1 << (( flags & 0x07) + 1));
+ if( !hasGlobalColorMap )
+ nColors = nLocalColors;
+ localPalette = new byte[ nLocalColors * 3 ];
+ if( in.read( localPalette ) != nLocalColors * 3 )
+ throw new IOException("Couldn't read color map.");
+ }
+ }
+
+ /**
+ * Returns the image's palette in raw format
+ * (r0,g0,b0,r1,g1,b2..r(Ncolors-1),g(Ncolors-1),b(Ncolors-1))
+ */
+ public byte[] getRawPalette()
+ {
+ return hasGlobalColorMap ? globalPalette : localPalette;
+ }
+
+ /**
+ * Returns the image file for animated gifs.
+ */
+ public GIFFile getImage( int index )
+ {
+ if( index == 0 )
+ return this;
+ if( animationFrames == null )
+ throw new ArrayIndexOutOfBoundsException("Only one image in file");
+ return (GIFFile)animationFrames.elementAt( index - 1 );
+ }
+
+ /**
+ * Return the image's raw image data.
+ * If the color depth is 1,2 or 4 bits per pixel the pixels are packed
+ * and the scanlines padded up to the nearest byte if needed.
+ */
+ public byte[] getRawImage()
+ {
+ return raster;
+ }
+
+ /**
+ * Return the number of images in the GIF file
+ */
+ public int nImages()
+ {
+ if( animationFrames != null )
+ return 1 + animationFrames.size();
+ return 1;
+ }
+
+ /**
+ * Handles extension blocks.
+ */
+ private void readExtension(InputStream in) throws IOException, GIFException
+ {
+ int functionCode = in.read();
+ byte[] data = readData(in);
+ switch( functionCode )
+ {
+ case EXTENSION_COMMENT: // comment block
+ comment = new String(data, "8859_1");
+ break;
+
+ case EXTENSION_GCONTROL: // Graphics control extension
+ undraw = (data[0] & 0x1C) >> 2;
+ // allegedly there can be bad values of this.
+ if( undraw < 1 && undraw > 3 ) undraw = 1;
+ hasTransparency = ((data[0] & 0x01) == 1);
+ transparentIndex = (data[3] & 0xFF);
+ duration = ((data[2] & 0xFF) << 8) | (data[1] & 0xFF);
+ break;
+
+ // Application extension. We only parse the Netscape animation
+ // extension here. Which is the only one most use anyway.
+ case EXTENSION_APPLICATION:
+ boolean isNS = true;
+ for(int i = 0; i < nsBlock.length; i++ )
+ if( nsBlock[i] != data[i] )
+ isNS = false;
+ if( isNS )
+ {
+ isLooped = true;
+ loops = ((data[12] & 0xFF) << 8) | (data[13] & 0xFF);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Reads a series of data blocks and merges them into a single one.
+ */
+ private byte[] readData(InputStream in) throws IOException
+ {
+ Vector v = new Vector();
+ int totalBytes = 0;
+
+ int n = in.read();
+ do
+ {
+ totalBytes += n;
+ byte[] block = new byte[ n ];
+ in.read(block);
+ v.add(block);
+ n = in.read();
+ }
+ while( n > 0 );
+
+ n = 0;
+ byte[] bigBuffer = new byte[ totalBytes ];
+ for( int i = 0; i < v.size(); i++ )
+ {
+ byte[] block = (byte[])v.elementAt(i);
+ System.arraycopy(block, 0, bigBuffer, n, block.length);
+ n += block.length;
+ }
+ return bigBuffer;
+ }
+
+ /**
+ * Loads a compressed image block and decompresses it.
+ */
+ private void decodeRaster(InputStream in) throws IOException
+ {
+ int initialCodeSize = in.read();
+ compressedData = readData( in );
+ dataBlockIndex = 0;
+
+ int rasterIndex = 0; // Index into the raster
+ int clearCode = (1 << initialCodeSize); // 256 usually
+ int endCode = clearCode + 1; // The stop code.
+
+ raster = new byte[ width * height ];
+
+ int codeSize = initialCodeSize + 1;
+ int code = getBits( codeSize ); // = clear
+ int nextCode = endCode + 1;
+
+ /*
+ * Initialize LZW dictionary
+ *
+ * First index - code #
+ * Second index:
+ * 0 = color index
+ * 1 = parent (-1 - no parent)
+ * 2 = first value
+ * 3 - depth
+ * The latter two aren't strictly necessary but make things faster, since
+ * copying the values forward is faster than going back and looking.
+ */
+ short[][] dictionary = new short[ 4096 ][ 4 ];
+
+ for(short i = 0; i < nColors; i ++ )
+ {
+ dictionary[i][0] = i; // color index
+ dictionary[i][1] = -1; // parent
+ dictionary[i][2] = i; // first
+ dictionary[i][3] = 1; // depth
+ }
+
+ code = getBits( codeSize ); // get second code
+ raster[ rasterIndex++ ] = (byte)dictionary[code][0];
+ int old = code;
+ code = getBits( codeSize ); // start at the third code
+ int c;
+
+ do
+ {
+ if( code == clearCode )
+ {
+ codeSize = initialCodeSize + 1;
+ nextCode = endCode + 1;
+ // get and output second code
+ code = getBits( codeSize );
+ raster[ rasterIndex++ ] = (byte)dictionary[code][0];
+ old = code;
+ }
+ else
+ {
+ dictionary[nextCode][1] = (short)old; // parent = old
+ dictionary[nextCode][2] = dictionary[old][2]; // first pixel
+ dictionary[nextCode][3] = (short)(dictionary[old][3] + 1); // depth
+
+ // appended pixel = first pixel of c
+ if( code < nextCode )
+ {
+ dictionary[nextCode][0] = dictionary[code][2];
+ old = code;
+ }
+ else // first of old
+ {
+ dictionary[nextCode][0] = dictionary[old][2];
+ old = nextCode;
+ }
+
+ c = old;
+ // output the code c
+ int depth = dictionary[c][3];
+ for( int i = depth - 1; i >= 0; i-- )
+ {
+ raster[ rasterIndex + i ] = (byte)dictionary[c][0];
+ c = dictionary[c][1]; // go to parent.
+ }
+ rasterIndex += depth;
+ nextCode ++;
+
+ if( codeSize < 12 && nextCode >= (1 << codeSize) )
+ codeSize++;
+ }
+ code = getBits( codeSize );
+ }
+ while( code != endCode && dataBlockIndex < compressedData.length );
+
+ compressedData = null; // throw away compressed data.
+ }
+
+ /**
+ * Returns nbits number of bits (in the LSBs) from compressedData
+ */
+ private int getBits( int nbits )
+ {
+ while( nbits > remainingBits )
+ {
+ int c = (compressedData[ dataBlockIndex++ ] & 0xFF) << remainingBits;
+ currentBits |= c;
+ remainingBits += 8;
+ }
+ int rval = (currentBits & ((1 << nbits) - 1));
+ currentBits = (currentBits >> nbits);
+ remainingBits -= nbits;
+ return rval;
+ }
+
+ /**
+ * Generic exception used by GIFFile to report decoding errors.
+ */
+ public static class GIFException extends Exception
+ {
+ public GIFException(String message)
+ {
+ super(message);
+ }
+ }
+}
diff --git a/gnu/javax/imageio/gif/GIFImageReader.java b/gnu/javax/imageio/gif/GIFImageReader.java
new file mode 100644
index 000000000..24e3d46db
--- /dev/null
+++ b/gnu/javax/imageio/gif/GIFImageReader.java
@@ -0,0 +1,242 @@
+/* GIFImageReader.java --
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.javax.imageio.gif;
+
+import java.io.IOException;
+import java.io.InputStream;
+import javax.imageio.*;
+import javax.imageio.spi.*;
+import javax.imageio.metadata.*;
+import javax.imageio.stream.ImageInputStream;
+import java.util.Iterator;
+import java.awt.image.BufferedImage;
+import java.awt.image.IndexColorModel;
+import java.awt.image.SampleModel;
+import java.awt.image.MultiPixelPackedSampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+
+public class GIFImageReader extends ImageReader
+{
+ private GIFFile file;
+
+ protected GIFImageReader(ImageReaderSpi originatingProvider)
+ {
+ super( originatingProvider );
+ file = null;
+ }
+
+ private void readImage() throws IOException
+ {
+ if( file != null )
+ return;
+
+ try
+ {
+ if( input instanceof InputStream )
+ file = new GIFFile( (InputStream)input );
+ else
+ file = new GIFFile( new GIFStream((ImageInputStream)input) );
+ }
+ catch(GIFFile.GIFException ge)
+ {
+ throw new IIOException(ge.getMessage());
+ }
+ }
+
+ /**
+ * Returns the Global/Local palette as an IndexColorModel
+ */
+ private IndexColorModel getPalette(int index)
+ {
+ GIFFile f = file.getImage( index );
+ byte[] data = f.getRawPalette();
+ int nc = f.getNColors();
+ byte[] r = new byte[nc];
+ byte[] g = new byte[nc];
+ byte[] b = new byte[nc];
+
+ for(int i = 0; i < nc; i ++ )
+ {
+ r[i] = data[ i * 3 ];
+ g[i] = data[ i * 3 + 1 ];
+ b[i] = data[ i * 3 + 2 ];
+ }
+
+ if( f.hasTransparency() )
+ {
+ byte[] a = new byte[nc];
+ for(int i = 0; i < nc; i ++ )
+ a[i] = (byte)0xFF;
+ a[f.getTransparentIndex()] = 0;
+ return new IndexColorModel(8, nc, r, g, b, a);
+ }
+
+ return new IndexColorModel(8, nc, r, g, b);
+ }
+
+ private void validateIndex(int imageIndex)
+ throws IndexOutOfBoundsException
+ {
+ if( imageIndex < 0 || imageIndex >= getNumImages(false) )
+ throw new IndexOutOfBoundsException("Invalid image index.");
+ }
+
+ public void setInput(Object input)
+ {
+ super.setInput(input);
+ }
+
+ public void setInput(Object input,
+ boolean seekForwardOnly,
+ boolean ignoreMetadata)
+ {
+ super.setInput(input, seekForwardOnly, ignoreMetadata);
+ }
+
+ public void setInput(Object input, boolean isStreamable)
+ {
+ super.setInput(input, isStreamable);
+
+ if (!(input instanceof ImageInputStream) &&
+ !(input instanceof InputStream))
+ throw new IllegalArgumentException("Input not an ImageInputStream.");
+ }
+
+ private void checkStream() throws IOException
+ {
+ if (!(input instanceof ImageInputStream) &&
+ !(input instanceof InputStream))
+ throw new IllegalStateException("Input not an ImageInputStream or InputStream.");
+
+ if(input == null)
+ throw new IllegalStateException("No input stream.");
+ }
+
+ public int getWidth(int imageIndex) throws IOException
+ {
+ validateIndex( imageIndex );
+ return file.getImage( imageIndex ).getWidth();
+ }
+
+ public int getHeight(int imageIndex) throws IOException
+ {
+ validateIndex( imageIndex );
+ return file.getImage( imageIndex ).getHeight();
+ }
+
+ public Iterator getImageTypes(int imageIndex)
+ {
+ validateIndex( imageIndex );
+ return null;
+ }
+
+ /**
+ * Returns the number of images.
+ */
+ public int getNumImages(boolean allowSearch)
+ {
+ try // Image should be loaded here already. But just in case:
+ {
+ readImage();
+ }
+ catch(IOException ioe)
+ {
+ return 0; // Well, now we're in trouble. But return something anyway.
+ }
+ return file.nImages();
+ }
+
+
+ // FIXME: Support metadata
+ public IIOMetadata getImageMetadata(int imageIndex)
+ {
+ validateIndex( imageIndex );
+ return null;
+ }
+
+ // FIXME: Support metadata
+ public IIOMetadata getStreamMetadata()
+ {
+ return null;
+ }
+
+ /**
+ * Reads the image indexed by imageIndex and returns it as
+ * a complete BufferedImage, using a supplied ImageReadParam.
+ */
+ public BufferedImage read(int imageIndex, ImageReadParam param)
+ throws IOException, IIOException
+ {
+ validateIndex( imageIndex );
+ GIFFile f = file.getImage( imageIndex );
+ int width = f.getWidth();
+ int height = f.getHeight();
+ SampleModel sm;
+ switch( f.getNColors() )
+ {
+ case 16:
+ sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
+ width, height, 4);
+ break;
+ case 4:
+ sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
+ width, height, 2);
+ break;
+ case 2:
+ sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
+ width, height, 1);
+ break;
+ default:
+ sm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_BYTE,
+ width, height,
+ new int[] {0xFF});
+ break;
+ }
+ byte[] bits = f.getRawImage();
+ for(int i = 0; i < 5; i++)
+ System.out.println("Bits "+i+":"+bits[i]);
+ DataBuffer db = new DataBufferByte(f.getRawImage(), width * height, 0);
+ WritableRaster raster = Raster.createWritableRaster(sm, db, null);
+
+ return new BufferedImage(getPalette( imageIndex ), raster, false, null);
+ }
+}
diff --git a/gnu/javax/imageio/gif/GIFImageReaderSpi.java b/gnu/javax/imageio/gif/GIFImageReaderSpi.java
new file mode 100644
index 000000000..a7b3ceb03
--- /dev/null
+++ b/gnu/javax/imageio/gif/GIFImageReaderSpi.java
@@ -0,0 +1,122 @@
+/* GIFImageReaderSpi.java --
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.javax.imageio.gif;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Locale;
+import javax.imageio.ImageReader;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.stream.ImageInputStream;
+
+public class GIFImageReaderSpi extends ImageReaderSpi
+{
+ static final String vendorName = "GNU";
+ static final String version = "0.1";
+ static final String readerClassName =
+ "gnu.javax.imageio.gif.GIFImageReader";
+ static final String[] names = { "Compuserve GIF" };
+ static final String[] suffixes = { ".gif" };
+ static final String[] MIMETypes = {
+ "image/gif",
+ "image/x-gif"}; // Not sure this is legal, but it seems to be used a bit
+ static final String[] writerSpiNames = null;
+ static final boolean supportsStandardStreamMetadataFormat = false;
+ static final String nativeStreamMetadataFormatName = null;
+ static final String nativeStreamMetadataFormatClassName = null;
+ static final String[] extraStreamMetadataFormatNames = null;
+ static final String[] extraStreamMetadataFormatClassNames = null;
+ static final boolean supportsStandardImageMetadataFormat = false;
+ static final String nativeImageMetadataFormatName = null;
+ static final String nativeImageMetadataFormatClassName = null;
+ static final String[] extraImageMetadataFormatNames = null;
+ static final String[] extraImageMetadataFormatClassNames = null;
+
+ public GIFImageReaderSpi()
+ {
+ super(vendorName, version,
+ names, suffixes, MIMETypes,
+ readerClassName,
+ new Class[]{ ImageInputStream.class, InputStream.class },
+ writerSpiNames,
+ supportsStandardStreamMetadataFormat,
+ nativeStreamMetadataFormatName,
+ nativeStreamMetadataFormatClassName,
+ extraStreamMetadataFormatNames,
+ extraStreamMetadataFormatClassNames,
+ supportsStandardImageMetadataFormat,
+ nativeImageMetadataFormatName,
+ nativeImageMetadataFormatClassName,
+ extraImageMetadataFormatNames,
+ extraImageMetadataFormatClassNames);
+ }
+
+ public String getDescription(Locale locale)
+ {
+ return "Compuserve GIF";
+ }
+
+ public boolean canDecodeInput(Object input)
+ throws IOException
+ {
+ if( input == null )
+ throw new IllegalArgumentException("Input object cannot be null.");
+
+ if( !(input instanceof ImageInputStream) &&
+ !(input instanceof InputStream))
+ return false;
+
+ boolean retval;
+ InputStream in;
+ if( input instanceof ImageInputStream )
+ in = new GIFStream( (ImageInputStream)input );
+ else
+ in = (InputStream)input;
+
+ in.mark(10); // we read 6 bytes
+ retval = GIFFile.readSignature( in );
+ in.reset();
+
+ return retval;
+ }
+
+ public ImageReader createReaderInstance(Object extension)
+ {
+ return new GIFImageReader(this);
+ }
+}
diff --git a/gnu/javax/imageio/gif/GIFStream.java b/gnu/javax/imageio/gif/GIFStream.java
new file mode 100644
index 000000000..630d587b8
--- /dev/null
+++ b/gnu/javax/imageio/gif/GIFStream.java
@@ -0,0 +1,102 @@
+/* GIFStream.java --
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.javax.imageio.gif;
+
+import java.io.InputStream;
+import java.io.IOException;
+import javax.imageio.stream.ImageInputStream;
+
+/**
+ * Implements InputStream on an ImageInputStream
+ * The purpose of this is to avoid IIO dependencies in GIFFile
+ * (which only uses read() anyway).
+ */
+public class GIFStream extends InputStream
+{
+ private ImageInputStream is;
+
+ public GIFStream( ImageInputStream is )
+ {
+ this.is = is;
+ }
+
+ public int available()
+ {
+ return 0;
+ }
+
+ public void close() throws IOException
+ {
+ is.close();
+ }
+
+ public void mark(int readlimit)
+ {
+ is.mark();
+ }
+
+ public boolean markSupported()
+ {
+ return true;
+ }
+
+ public int read() throws IOException
+ {
+ return is.read();
+ }
+
+ public int read(byte[] b) throws IOException
+ {
+ return is.read(b);
+ }
+
+ public int read(byte[] b, int offset, int length) throws IOException
+ {
+ return is.read(b, offset, length);
+ }
+
+ public void reset() throws IOException
+ {
+ is.reset();
+ }
+
+ public long skip(long n) throws IOException
+ {
+ return is.skipBytes(n);
+ }
+}
diff --git a/javax/imageio/spi/IIORegistry.java b/javax/imageio/spi/IIORegistry.java
index 3e9b2c289..fae75239a 100644
--- a/javax/imageio/spi/IIORegistry.java
+++ b/javax/imageio/spi/IIORegistry.java
@@ -45,6 +45,7 @@ import java.awt.Toolkit;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import gnu.javax.imageio.gif.GIFImageReaderSpi;
public final class IIORegistry extends ServiceRegistry
{
@@ -81,7 +82,7 @@ public final class IIORegistry extends ServiceRegistry
super(defaultCategories.iterator());
// XXX: Register built-in Spis here.
-
+ registerServiceProvider(new GIFImageReaderSpi()); // Register GIF decoder
Toolkit toolkit = Toolkit.getDefaultToolkit();
if (toolkit instanceof ClasspathToolkit)
((ClasspathToolkit)toolkit).registerImageIOSpis(this);