diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/gnu/javax/imageio/gif/GIFFile.java | |
download | cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2 cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/gnu/javax/imageio/gif/GIFFile.java')
-rw-r--r-- | libjava/classpath/gnu/javax/imageio/gif/GIFFile.java | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/javax/imageio/gif/GIFFile.java b/libjava/classpath/gnu/javax/imageio/gif/GIFFile.java new file mode 100644 index 000000000..941397a0e --- /dev/null +++ b/libjava/classpath/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 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); + } + } +} |