1028 lines · 35.5 KB · Java
RawDownload
/* * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.imageio.plugins.gif; import java.awt.Point;import java.awt.Rectangle;import java.awt.image.BufferedImage;import java.awt.image.DataBuffer;import java.awt.image.WritableRaster;import java.io.BufferedInputStream;import java.io.DataInputStream;import java.io.EOFException;import java.io.InputStream;import java.io.IOException;import java.nio.ByteOrder;import java.util.ArrayList;import java.util.Arrays;import java.util.Iterator;import java.util.List;import javax.imageio.IIOException;import javax.imageio.ImageReader;import javax.imageio.ImageReadParam;import javax.imageio.ImageTypeSpecifier;import javax.imageio.metadata.IIOMetadata;import javax.imageio.spi.ImageReaderSpi;import javax.imageio.stream.ImageInputStream;import com.sun.imageio.plugins.common.ReaderUtil; public class GIFImageReader extends ImageReader {     // The current ImageInputStream source.    ImageInputStream stream = null;     // Per-stream settings     // True if the file header including stream metadata has been read.    boolean gotHeader = false;     // Global metadata, read once per input setting.    GIFStreamMetadata streamMetadata = null;     // The current image index    int currIndex = -1;     // Metadata for image at 'currIndex', or null.    GIFImageMetadata imageMetadata = null;     // A List of Longs indicating the stream positions of the    // start of the metadata for each image.  Entries are added    // as needed.    List imageStartPosition = new ArrayList();     // Length of metadata for image at 'currIndex', valid only if    // imageMetadata != null.    int imageMetadataLength;     // The number of images in the stream, if known, otherwise -1.    int numImages = -1;     // Variables used by the LZW decoding process    byte[] block = new byte[255];    int blockLength = 0;    int bitPos = 0;    int nextByte = 0;    int initCodeSize;    int clearCode;    int eofCode;     // 32-bit lookahead buffer    int next32Bits = 0;     // Try if the end of the data blocks has been found,    // and we are simply draining the 32-bit buffer    boolean lastBlockFound = false;     // The image to be written.    BufferedImage theImage = null;     // The image's tile.    WritableRaster theTile = null;     // The image dimensions (from the stream).    int width = -1, height = -1;     // The pixel currently being decoded (in the stream's coordinates).    int streamX = -1, streamY = -1;     // The number of rows decoded    int rowsDone = 0;     // The current interlace pass, starting with 0.    int interlacePass = 0;     // End per-stream settings     // Constants used to control interlacing.    static final int[] interlaceIncrement = { 8, 8, 4, 2, -1 };    static final int[] interlaceOffset = { 0, 4, 2, 1, -1 };     public GIFImageReader(ImageReaderSpi originatingProvider) {        super(originatingProvider);    }     // Take input from an ImageInputStream    public void setInput(Object input,                         boolean seekForwardOnly,                         boolean ignoreMetadata) {        super.setInput(input, seekForwardOnly, ignoreMetadata);        if (input != null) {            if (!(input instanceof ImageInputStream)) {                throw new IllegalArgumentException                    ("input not an ImageInputStream!");            }            this.stream = (ImageInputStream)input;        } else {            this.stream = null;        }         // Clear all values based on the previous stream contents        resetStreamSettings();    }     public int getNumImages(boolean allowSearch) throws IIOException {        if (stream == null) {            throw new IllegalStateException("Input not set!");        }        if (seekForwardOnly && allowSearch) {            throw new IllegalStateException                ("seekForwardOnly and allowSearch can't both be true!");        }         if (numImages > 0) {            return numImages;        }        if (allowSearch) {            this.numImages = locateImage(Integer.MAX_VALUE) + 1;        }        return numImages;    }     // Throw an IndexOutOfBoundsException if index < minIndex,    // and bump minIndex if required.    private void checkIndex(int imageIndex) {        if (imageIndex < minIndex) {            throw new IndexOutOfBoundsException("imageIndex < minIndex!");        }        if (seekForwardOnly) {            minIndex = imageIndex;        }    }     public int getWidth(int imageIndex) throws IIOException {        checkIndex(imageIndex);         int index = locateImage(imageIndex);        if (index != imageIndex) {            throw new IndexOutOfBoundsException();        }        readMetadata();        return imageMetadata.imageWidth;    }     public int getHeight(int imageIndex) throws IIOException {        checkIndex(imageIndex);         int index = locateImage(imageIndex);        if (index != imageIndex) {            throw new IndexOutOfBoundsException();        }        readMetadata();        return imageMetadata.imageHeight;    }     public Iterator getImageTypes(int imageIndex) throws IIOException {        checkIndex(imageIndex);         int index = locateImage(imageIndex);        if (index != imageIndex) {            throw new IndexOutOfBoundsException();        }        readMetadata();         List l = new ArrayList(1);         byte[] colorTable;        if (imageMetadata.localColorTable != null) {            colorTable = imageMetadata.localColorTable;        } else {            colorTable = streamMetadata.globalColorTable;        }         // Normalize color table length to 2^1, 2^2, 2^4, or 2^8        int length = colorTable.length/3;        int bits;        if (length == 2) {            bits = 1;        } else if (length == 4) {            bits = 2;        } else if (length == 8 || length == 16) {            // Bump from 3 to 4 bits            bits = 4;        } else {            // Bump to 8 bits            bits = 8;        }        int lutLength = 1 << bits;        byte[] r = new byte[lutLength];        byte[] g = new byte[lutLength];        byte[] b = new byte[lutLength];         // Entries from length + 1 to lutLength - 1 will be 0        int rgbIndex = 0;        for (int i = 0; i < length; i++) {            r[i] = colorTable[rgbIndex++];            g[i] = colorTable[rgbIndex++];            b[i] = colorTable[rgbIndex++];        }         byte[] a = null;        if (imageMetadata.transparentColorFlag) {            a = new byte[lutLength];            Arrays.fill(a, (byte)255);             // Some files erroneously have a transparent color index            // of 255 even though there are fewer than 256 colors.            int idx = Math.min(imageMetadata.transparentColorIndex,                               lutLength - 1);            a[idx] = (byte)0;        }         int[] bitsPerSample = new int[1];        bitsPerSample[0] = bits;        l.add(ImageTypeSpecifier.createIndexed(r, g, b, a, bits,                                               DataBuffer.TYPE_BYTE));        return l.iterator();    }     public ImageReadParam getDefaultReadParam() {        return new ImageReadParam();    }     public IIOMetadata getStreamMetadata() throws IIOException {        readHeader();        return streamMetadata;    }     public IIOMetadata getImageMetadata(int imageIndex) throws IIOException {        checkIndex(imageIndex);         int index = locateImage(imageIndex);        if (index != imageIndex) {            throw new IndexOutOfBoundsException("Bad image index!");        }        readMetadata();        return imageMetadata;    }     // BEGIN LZW STUFF     private void initNext32Bits() {        next32Bits = block[0] & 0xff;        next32Bits |= (block[1] & 0xff) << 8;        next32Bits |= (block[2] & 0xff) << 16;        next32Bits |= block[3] << 24;        nextByte = 4;    }     // Load a block (1-255 bytes) at a time, and maintain    // a 32-bit lookahead buffer that is filled from the left    // and extracted from the right.    //    // When the last block is found, we continue to    //    private int getCode(int codeSize, int codeMask) throws IOException {        if (bitPos + codeSize > 32) {            return eofCode; // No more data available        }         int code = (next32Bits >> bitPos) & codeMask;        bitPos += codeSize;         // Shift in a byte of new data at a time        while (bitPos >= 8 && !lastBlockFound) {            next32Bits >>>= 8;            bitPos -= 8;             // Check if current block is out of bytes            if (nextByte >= blockLength) {                // Get next block size                blockLength = stream.readUnsignedByte();                if (blockLength == 0) {                    lastBlockFound = true;                    return code;                } else {                    int left = blockLength;                    int off = 0;                    while (left > 0) {                        int nbytes = stream.read(block, off, left);                        off += nbytes;                        left -= nbytes;                    }                    nextByte = 0;                }            }             next32Bits |= block[nextByte++] << 24;        }         return code;    }     public void initializeStringTable(int[] prefix,                                      byte[] suffix,                                      byte[] initial,                                      int[] length) {        int numEntries = 1 << initCodeSize;        for (int i = 0; i < numEntries; i++) {            prefix[i] = -1;            suffix[i] = (byte)i;            initial[i] = (byte)i;            length[i] = 1;        }         // Fill in the entire table for robustness against        // out-of-sequence codes.        for (int i = numEntries; i < 4096; i++) {            prefix[i] = -1;            length[i] = 1;        }         // tableIndex = numEntries + 2;        // codeSize = initCodeSize + 1;        // codeMask = (1 << codeSize) - 1;    }     Rectangle sourceRegion;    int sourceXSubsampling;    int sourceYSubsampling;    int sourceMinProgressivePass;    int sourceMaxProgressivePass;     Point destinationOffset;    Rectangle destinationRegion;     // Used only if IIOReadUpdateListeners are present    int updateMinY;    int updateYStep;     boolean decodeThisRow = true;    int destY = 0;     byte[] rowBuf;     private void outputRow() {        // Clip against ImageReadParam        int width = Math.min(sourceRegion.width,                             destinationRegion.width*sourceXSubsampling);        int destX = destinationRegion.x;         if (sourceXSubsampling == 1) {            theTile.setDataElements(destX, destY, width, 1, rowBuf);        } else {            for (int x = 0; x < width; x += sourceXSubsampling, destX++) {                theTile.setSample(destX, destY, 0, rowBuf[x] & 0xff);            }        }         // Update IIOReadUpdateListeners, if any        if (updateListeners != null) {            int[] bands = { 0 };            // updateYStep will have been initialized if            // updateListeners is non-null            processImageUpdate(theImage,                               destX, destY,                               width, 1, 1, updateYStep,                               bands);        }    }     private void computeDecodeThisRow() {        this.decodeThisRow =            (destY < destinationRegion.y + destinationRegion.height) &&            (streamY >= sourceRegion.y) &&            (streamY < sourceRegion.y + sourceRegion.height) &&            (((streamY - sourceRegion.y) % sourceYSubsampling) == 0);    }     private void outputPixels(byte[] string, int len) {        if (interlacePass < sourceMinProgressivePass ||            interlacePass > sourceMaxProgressivePass) {            return;        }         for (int i = 0; i < len; i++) {            if (streamX >= sourceRegion.x) {                rowBuf[streamX - sourceRegion.x] = string[i];            }             // Process end-of-row            ++streamX;            if (streamX == width) {                // Update IIOReadProgressListeners                ++rowsDone;                processImageProgress(100.0F*rowsDone/height);                 if (decodeThisRow) {                    outputRow();                }                 streamX = 0;                if (imageMetadata.interlaceFlag) {                    streamY += interlaceIncrement[interlacePass];                    if (streamY >= height) {                        // Inform IIOReadUpdateListeners of end of pass                        if (updateListeners != null) {                            processPassComplete(theImage);                        }                         ++interlacePass;                        if (interlacePass > sourceMaxProgressivePass) {                            return;                        }                        streamY = interlaceOffset[interlacePass];                        startPass(interlacePass);                    }                } else {                    ++streamY;                }                 // Determine whether pixels from this row will                // be written to the destination                this.destY = destinationRegion.y +                    (streamY - sourceRegion.y)/sourceYSubsampling;                computeDecodeThisRow();            }        }    }     // END LZW STUFF     private void readHeader() throws IIOException {        if (gotHeader) {            return;        }        if (stream == null) {            throw new IllegalStateException("Input not set!");        }         // Create an object to store the stream metadata        this.streamMetadata = new GIFStreamMetadata();         try {            stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);             byte[] signature = new byte[6];            stream.readFully(signature);             StringBuffer version = new StringBuffer(3);            version.append((char)signature[3]);            version.append((char)signature[4]);            version.append((char)signature[5]);            streamMetadata.version = version.toString();             streamMetadata.logicalScreenWidth = stream.readUnsignedShort();            streamMetadata.logicalScreenHeight = stream.readUnsignedShort();             int packedFields = stream.readUnsignedByte();            boolean globalColorTableFlag = (packedFields & 0x80) != 0;            streamMetadata.colorResolution = ((packedFields >> 4) & 0x7) + 1;            streamMetadata.sortFlag = (packedFields & 0x8) != 0;            int numGCTEntries = 1 << ((packedFields & 0x7) + 1);             streamMetadata.backgroundColorIndex = stream.readUnsignedByte();            streamMetadata.pixelAspectRatio = stream.readUnsignedByte();             if (globalColorTableFlag) {                streamMetadata.globalColorTable = new byte[3*numGCTEntries];

Lines 1–500 of 1028.