Use Tree Navigation
public class

GIFImageReader

extends ImageReader
/*
 * Copyright (c) 2000, 2007, 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];
                stream
.readFully(streamMetadata.globalColorTable);
           
} else {
                streamMetadata
.globalColorTable = null;
           
}

           
// Found position of metadata for image 0
            imageStartPosition
.add(new Long(stream.getStreamPosition()));
       
} catch (IOException e) {
           
throw new IIOException("I/O error reading header!", e);
       
}

        gotHeader
= true;
   
}

   
private boolean skipImage() throws IIOException {
       
// Stream must be at the beginning of an image descriptor
       
// upon exit

       
try {
           
while (true) {
               
int blockType = stream.readUnsignedByte();

               
if (blockType == 0x2c) {
                    stream
.skipBytes(8);

                   
int packedFields = stream.readUnsignedByte();
                   
if ((packedFields & 0x80) != 0) {
                       
// Skip color table if any
                       
int bits = (packedFields & 0x7) + 1;
                        stream
.skipBytes(3*(1 << bits));
                   
}

                    stream
.skipBytes(1);

                   
int length = 0;
                   
do {
                        length
= stream.readUnsignedByte();
                        stream
.skipBytes(length);
                   
} while (length > 0);

                   
return true;
               
} else if (blockType == 0x3b) {
                   
return false;
               
} else if (blockType == 0x21) {
                   
int label = stream.readUnsignedByte();

                   
int length = 0;
                   
do {
                        length
= stream.readUnsignedByte();
                        stream
.skipBytes(length);
                   
} while (length > 0);
               
} else if (blockType == 0x0) {
                   
// EOF
                   
return false;
               
} else {
                   
int length = 0;
                   
do {
                        length
= stream.readUnsignedByte();
                        stream
.skipBytes(length);
                   
} while (length > 0);
               
}
           
}
       
} catch (EOFException e) {
           
return false;
       
} catch (IOException e) {
           
throw new IIOException("I/O error locating image!", e);
       
}
   
}

   
private int locateImage(int imageIndex) throws IIOException {
        readHeader
();

       
try {
           
// Find closest known index
           
int index = Math.min(imageIndex, imageStartPosition.size() - 1);

           
// Seek to that position
           
Long l = (Long)imageStartPosition.get(index);
            stream
.seek(l.longValue());

           
// Skip images until at desired index or last image found
           
while (index < imageIndex) {
               
if (!skipImage()) {
                   
--index;
                   
return index;
               
}

               
Long l1 = new Long(stream.getStreamPosition());
                imageStartPosition
.add(l1);
               
++index;
           
}
       
} catch (IOException e) {
           
throw new IIOException("Couldn't seek!", e);
       
}

       
if (currIndex != imageIndex) {
            imageMetadata
= null;
       
}
        currIndex
= imageIndex;
       
return imageIndex;
   
}

   
// Read blocks of 1-255 bytes, stop at a 0-length block
   
private byte[] concatenateBlocks() throws IOException {
       
byte[] data = new byte[0];
       
while (true) {
           
int length = stream.readUnsignedByte();
           
if (length == 0) {
               
break;
           
}
           
byte[] newData = new byte[data.length + length];
           
System.arraycopy(data, 0, newData, 0, data.length);
            stream
.readFully(newData, data.length, length);
            data
= newData;
       
}

       
return data;
   
}

   
// Stream must be positioned at start of metadata for 'currIndex'
   
private void readMetadata() throws IIOException {
       
if (stream == null) {
           
throw new IllegalStateException("Input not set!");
       
}

       
try {
           
// Create an object to store the image metadata
           
this.imageMetadata = new GIFImageMetadata();

           
long startPosition = stream.getStreamPosition();
           
while (true) {
               
int blockType = stream.readUnsignedByte();
               
if (blockType == 0x2c) { // Image Descriptor
                    imageMetadata
.imageLeftPosition =
                        stream
.readUnsignedShort();
                    imageMetadata
.imageTopPosition =
                        stream
.readUnsignedShort();
                    imageMetadata
.imageWidth = stream.readUnsignedShort();
                    imageMetadata
.imageHeight = stream.readUnsignedShort();

                   
int idPackedFields = stream.readUnsignedByte();
                   
boolean localColorTableFlag =
                       
(idPackedFields & 0x80) != 0;
                    imageMetadata
.interlaceFlag = (idPackedFields & 0x40) != 0;
                    imageMetadata
.sortFlag = (idPackedFields & 0x20) != 0;
                   
int numLCTEntries = 1 << ((idPackedFields & 0x7) + 1);

                   
if (localColorTableFlag) {
                       
// Read color table if any
                        imageMetadata
.localColorTable =
                           
new byte[3*numLCTEntries];
                        stream
.readFully(imageMetadata.localColorTable);
                   
} else {
                        imageMetadata
.localColorTable = null;
                   
}

                   
// Record length of this metadata block
                   
this.imageMetadataLength =
                       
(int)(stream.getStreamPosition() - startPosition);

                   
// Now positioned at start of LZW-compressed pixels
                   
return;
               
} else if (blockType == 0x21) { // Extension block
                   
int label = stream.readUnsignedByte();

                   
if (label == 0xf9) { // Graphics Control Extension
                       
int gceLength = stream.readUnsignedByte(); // 4
                       
int gcePackedFields = stream.readUnsignedByte();
                        imageMetadata
.disposalMethod =
                           
(gcePackedFields >> 2) & 0x3;
                        imageMetadata
.userInputFlag =
                           
(gcePackedFields & 0x2) != 0;
                        imageMetadata
.transparentColorFlag =
                           
(gcePackedFields & 0x1) != 0;

                        imageMetadata
.delayTime = stream.readUnsignedShort();
                        imageMetadata
.transparentColorIndex
                           
= stream.readUnsignedByte();

                       
int terminator = stream.readUnsignedByte();
                   
} else if (label == 0x1) { // Plain text extension
                       
int length = stream.readUnsignedByte();
                        imageMetadata
.hasPlainTextExtension = true;
                        imageMetadata
.textGridLeft =
                            stream
.readUnsignedShort();
                        imageMetadata
.textGridTop =
                            stream
.readUnsignedShort();
                        imageMetadata
.textGridWidth =
                            stream
.readUnsignedShort();
                        imageMetadata
.textGridHeight =
                            stream
.readUnsignedShort();
                        imageMetadata
.characterCellWidth =
                            stream
.readUnsignedByte();
                        imageMetadata
.characterCellHeight =
                            stream
.readUnsignedByte();
                        imageMetadata
.textForegroundColor =
                            stream
.readUnsignedByte();
                        imageMetadata
.textBackgroundColor =
                            stream
.readUnsignedByte();
                        imageMetadata
.text = concatenateBlocks();
                   
} else if (label == 0xfe) { // Comment extension
                       
byte[] comment = concatenateBlocks();
                       
if (imageMetadata.comments == null) {
                            imageMetadata
.comments = new ArrayList();
                       
}
                        imageMetadata
.comments.add(comment);
                   
} else if (label == 0xff) { // Application extension
                       
int blockSize = stream.readUnsignedByte();
                       
byte[] applicationID = new byte[8];
                       
byte[] authCode = new byte[3];

                       
// read available data
                       
byte[] blockData = new byte[blockSize];
                        stream
.readFully(blockData);

                       
int offset = copyData(blockData, 0, applicationID);
                        offset
= copyData(blockData, offset, authCode);

                       
byte[] applicationData = concatenateBlocks();

                       
if (offset < blockSize) {
                           
int len = blockSize - offset;
                           
byte[] data =
                               
new byte[len + applicationData.length];

                           
System.arraycopy(blockData, offset, data, 0, len);
                           
System.arraycopy(applicationData, 0, data, len,
                                             applicationData
.length);

                            applicationData
= data;
                       
}

                       
// Init lists if necessary
                       
if (imageMetadata.applicationIDs == null) {
                            imageMetadata
.applicationIDs = new ArrayList();
                            imageMetadata
.authenticationCodes =
                               
new ArrayList();
                            imageMetadata
.applicationData = new ArrayList();
                       
}
                        imageMetadata
.applicationIDs.add(applicationID);
                        imageMetadata
.authenticationCodes.add(authCode);
                        imageMetadata
.applicationData.add(applicationData);
                   
} else {
                       
// Skip over unknown extension blocks
                       
int length = 0;
                       
do {
                            length
= stream.readUnsignedByte();
                            stream
.skipBytes(length);
                       
} while (length > 0);
                   
}
               
} else if (blockType == 0x3b) { // Trailer
                   
throw new IndexOutOfBoundsException
                       
("Attempt to read past end of image sequence!");
               
} else {
                   
throw new IIOException("Unexpected block type " +
                                           blockType
+ "!");
               
}
           
}
       
} catch (IIOException iioe) {
           
throw iioe;
       
} catch (IOException ioe) {
           
throw new IIOException("I/O error reading image metadata!", ioe);
       
}
   
}

   
private int copyData(byte[] src, int offset, byte[] dst) {
       
int len = dst.length;
       
int rest = src.length - offset;
       
if (len > rest) {
            len
= rest;
       
}
       
System.arraycopy(src, offset, dst, 0, len);
       
return offset + len;
   
}

   
private void startPass(int pass) {
       
if (updateListeners == null) {
           
return;
       
}

       
int y = 0;
       
int yStep = 1;
       
if (imageMetadata.interlaceFlag) {
            y
= interlaceOffset[interlacePass];
            yStep
= interlaceIncrement[interlacePass];
       
}

       
int[] vals = ReaderUtil.
            computeUpdatedPixels
(sourceRegion,
                                 destinationOffset
,
                                 destinationRegion
.x,
                                 destinationRegion
.y,
                                 destinationRegion
.x +
                                 destinationRegion
.width - 1,
                                 destinationRegion
.y +
                                 destinationRegion
.height - 1,
                                 sourceXSubsampling
,
                                 sourceYSubsampling
,
                                 
0,
                                 y
,
                                 destinationRegion
.width,
                                 
(destinationRegion.height + yStep - 1)/yStep,
                                 
1,
                                 yStep
);

       
// Initialized updateMinY and updateYStep
       
this.updateMinY = vals[1];
       
this.updateYStep = vals[5];

       
// Inform IIOReadUpdateListeners of new pass
       
int[] bands = { 0 };

        processPassStarted
(theImage,
                           interlacePass
,
                           sourceMinProgressivePass
,
                           sourceMaxProgressivePass
,
                           
0,
                           updateMinY
,
                           
1,
                           updateYStep
,
                           bands
);
   
}

   
public BufferedImage read(int imageIndex, ImageReadParam param)
       
throws IIOException {
       
if (stream == null) {
           
throw new IllegalStateException("Input not set!");
       
}
        checkIndex
(imageIndex);

       
int index = locateImage(imageIndex);
       
if (index != imageIndex) {
           
throw new IndexOutOfBoundsException("imageIndex out of bounds!");
       
}

        clearAbortRequest
();
        readMetadata
();

       
// A null ImageReadParam means we use the default
       
if (param == null) {
            param
= getDefaultReadParam();
       
}

       
// Initialize the destination image
       
Iterator imageTypes = getImageTypes(imageIndex);
       
this.theImage = getDestination(param,
                                       imageTypes
,
                                       imageMetadata
.imageWidth,
                                       imageMetadata
.imageHeight);
       
this.theTile = theImage.getWritableTile(0, 0);
       
this.width = imageMetadata.imageWidth;
       
this.height = imageMetadata.imageHeight;
       
this.streamX = 0;
       
this.streamY = 0;
       
this.rowsDone = 0;
       
this.interlacePass = 0;

       
// Get source region, taking subsampling offsets into account,
       
// and clipping against the true source bounds

       
this.sourceRegion = new Rectangle(0, 0, 0, 0);
       
this.destinationRegion = new Rectangle(0, 0, 0, 0);
        computeRegions
(param, width, height, theImage,
                       sourceRegion
, destinationRegion);
       
this.destinationOffset = new Point(destinationRegion.x,
                                           destinationRegion
.y);

       
this.sourceXSubsampling = param.getSourceXSubsampling();
       
this.sourceYSubsampling = param.getSourceYSubsampling();
       
this.sourceMinProgressivePass =
           
Math.max(param.getSourceMinProgressivePass(), 0);
       
this.sourceMaxProgressivePass =
           
Math.min(param.getSourceMaxProgressivePass(), 3);

       
this.destY = destinationRegion.y +
           
(streamY - sourceRegion.y)/sourceYSubsampling;
        computeDecodeThisRow
();

       
// Inform IIOReadProgressListeners of start of image
        processImageStarted
(imageIndex);
        startPass
(0);

       
this.rowBuf = new byte[width];

       
try {
           
// Read and decode the image data, fill in theImage
           
this.initCodeSize = stream.readUnsignedByte();

           
// Read first data block
           
this.blockLength = stream.readUnsignedByte();
           
int left = blockLength;
           
int off = 0;
           
while (left > 0) {
               
int nbytes = stream.read(block, off, left);
                left
-= nbytes;
                off
+= nbytes;
           
}

           
this.bitPos = 0;
           
this.nextByte = 0;
           
this.lastBlockFound = false;
           
this.interlacePass = 0;

           
// Init 32-bit buffer
            initNext32Bits
();

           
this.clearCode = 1 << initCodeSize;
           
this.eofCode = clearCode + 1;

           
int code, oldCode = 0;

           
int[] prefix = new int[4096];
           
byte[] suffix = new byte[4096];
           
byte[] initial = new byte[4096];
           
int[] length = new int[4096];
           
byte[] string = new byte[4096];

            initializeStringTable
(prefix, suffix, initial, length);
           
int tableIndex = (1 << initCodeSize) + 2;
           
int codeSize = initCodeSize + 1;
           
int codeMask = (1 << codeSize) - 1;

           
while (!abortRequested()) {
                code
= getCode(codeSize, codeMask);

               
if (code == clearCode) {
                    initializeStringTable
(prefix, suffix, initial, length);
                    tableIndex
= (1 << initCodeSize) + 2;
                    codeSize
= initCodeSize + 1;
                    codeMask
= (1 << codeSize) - 1;

                    code
= getCode(codeSize, codeMask);
                   
if (code == eofCode) {
                       
// Inform IIOReadProgressListeners of end of image
                        processImageComplete
();
                       
return theImage;
                   
}
               
} else if (code == eofCode) {
                   
// Inform IIOReadProgressListeners of end of image
                    processImageComplete
();
                   
return theImage;
               
} else {
                   
int newSuffixIndex;
                   
if (code < tableIndex) {
                        newSuffixIndex
= code;
                   
} else { // code == tableIndex
                        newSuffixIndex
= oldCode;
                       
if (code != tableIndex) {
                           
// warning - code out of sequence
                           
// possibly data corruption
                            processWarningOccurred
("Out-of-sequence code!");
                       
}
                   
}

                   
int ti = tableIndex;
                   
int oc = oldCode;

                    prefix
[ti] = oc;
                    suffix
[ti] = initial[newSuffixIndex];
                    initial
[ti] = initial[oc];
                    length
[ti] = length[oc] + 1;

                   
++tableIndex;
                   
if ((tableIndex == (1 << codeSize)) &&
                       
(tableIndex < 4096)) {
                       
++codeSize;
                        codeMask
= (1 << codeSize) - 1;
                   
}
               
}

               
// Reverse code
               
int c = code;
               
int len = length[c];
               
for (int i = len - 1; i >= 0; i--) {
                   
string[i] = suffix[c];
                    c
= prefix[c];
               
}

                outputPixels
(string, len);
                oldCode
= code;
           
}

            processReadAborted
();
           
return theImage;
       
} catch (IOException e) {
            e
.printStackTrace();
           
throw new IIOException("I/O error reading image!", e);
       
}
   
}

   
/**
     * Remove all settings including global settings such as
     * <code>Locale</code>s and listeners, as well as stream settings.
     */

   
public void reset() {
       
super.reset();
        resetStreamSettings
();
   
}

   
/**
     * Remove local settings based on parsing of a stream.
     */

   
private void resetStreamSettings() {
        gotHeader
= false;
        streamMetadata
= null;
        currIndex
= -1;
        imageMetadata
= null;
        imageStartPosition
= new ArrayList();
        numImages
= -1;

       
// No need to reinitialize 'block'
        blockLength
= 0;
        bitPos
= 0;
        nextByte
= 0;

        next32Bits
= 0;
        lastBlockFound
= false;

        theImage
= null;
        theTile
= null;
        width
= -1;
        height
= -1;
        streamX
= -1;
        streamY
= -1;
        rowsDone
= 0;
        interlacePass
= 0;
   
}
}