You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
630 lines
21 KiB
630 lines
21 KiB
5 years ago
|
package com.fr.third.JAI;
|
||
|
/*
|
||
|
* Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions are met:
|
||
|
*
|
||
|
* -Redistributions of source code must retain the above copyright notice, this
|
||
|
* list of conditions and the following disclaimer.
|
||
|
*
|
||
|
* -Redistribution in binary form must reproduct the above copyright notice,
|
||
|
* this list of conditions and the following disclaimer in the documentation
|
||
|
* and/or other materials provided with the distribution.
|
||
|
*
|
||
|
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
|
||
|
* be used to endorse or promote products derived from this software without
|
||
|
* specific prior written permission.
|
||
|
*
|
||
|
* This software is provided "AS IS," without a warranty of any kind. ALL
|
||
|
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
|
||
|
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
|
||
|
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
|
||
|
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
|
||
|
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
|
||
|
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
|
||
|
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
|
||
|
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
|
||
|
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
|
||
|
* POSSIBILITY OF SUCH DAMAGES.
|
||
|
*
|
||
|
* You acknowledge that Software is not designed,licensed or intended for use in
|
||
|
* the design, construction, operation or maintenance of any nuclear facility.
|
||
|
*/
|
||
|
|
||
|
import java.awt.Point;
|
||
|
import java.awt.Rectangle;
|
||
|
import java.awt.image.BufferedImage;
|
||
|
import java.awt.image.ColorModel;
|
||
|
import java.awt.image.DataBuffer;
|
||
|
import java.awt.image.ImageProducer;
|
||
|
import java.awt.image.IndexColorModel;
|
||
|
import java.awt.image.PixelInterleavedSampleModel;
|
||
|
import java.awt.image.Raster;
|
||
|
import java.awt.image.RenderedImage;
|
||
|
import java.awt.image.SampleModel;
|
||
|
import java.awt.image.WritableRaster;
|
||
|
import java.io.IOException;
|
||
|
import java.io.InputStream;
|
||
|
import java.util.Arrays;
|
||
|
import java.util.HashMap;
|
||
|
|
||
|
/**
|
||
|
*/
|
||
|
public class GIFImageDecoder extends ImageDecoderImpl {
|
||
|
|
||
|
// The global color table.
|
||
|
private byte[] globalColorTable = null;
|
||
|
|
||
|
// Whether the last page has been encountered.
|
||
|
private boolean maxPageFound = false;
|
||
|
|
||
|
// The maximum allowable page for reading.
|
||
|
private int maxPage;
|
||
|
|
||
|
// The previous page read.
|
||
|
private int prevPage = -1;
|
||
|
|
||
|
// The previous page on which getTile() was invoked in this object.
|
||
|
private int prevSyncedPage = -1;
|
||
|
|
||
|
// Map of Integer page numbers to RenderedImages.
|
||
|
private HashMap images = new HashMap();
|
||
|
|
||
|
/**
|
||
|
* Read the overall stream header and return the global color map
|
||
|
* or <code>null</code>.
|
||
|
*/
|
||
|
private static byte[] readHeader(SeekableStream input) throws IOException {
|
||
|
byte[] globalColorTable = null;
|
||
|
try {
|
||
|
// Skip the version string and logical screen dimensions.
|
||
|
input.skipBytes(10);
|
||
|
|
||
|
int packedFields = input.readUnsignedByte();
|
||
|
boolean globalColorTableFlag = (packedFields & 0x80) != 0;
|
||
|
int numGCTEntries = 1 << ((packedFields & 0x7) + 1);
|
||
|
|
||
|
int backgroundColorIndex = input.readUnsignedByte();
|
||
|
|
||
|
// Read the aspect ratio but ignore the returned value.
|
||
|
input.read();
|
||
|
|
||
|
if (globalColorTableFlag) {
|
||
|
globalColorTable = new byte[3*numGCTEntries];
|
||
|
input.readFully(globalColorTable);
|
||
|
} else {
|
||
|
globalColorTable = null;
|
||
|
}
|
||
|
} catch (IOException e) {
|
||
|
throw new IOException(JaiI18N.getString("GIFImageDecoder0"));
|
||
|
}
|
||
|
|
||
|
return globalColorTable;
|
||
|
}
|
||
|
|
||
|
public GIFImageDecoder(SeekableStream input,
|
||
|
ImageDecodeParam param) {
|
||
|
super(input, param);
|
||
|
}
|
||
|
|
||
|
public GIFImageDecoder(InputStream input,
|
||
|
ImageDecodeParam param) {
|
||
|
super(input, param);
|
||
|
}
|
||
|
|
||
|
public int getNumPages() throws IOException {
|
||
|
int page = prevPage + 1;
|
||
|
|
||
|
while(!maxPageFound) {
|
||
|
try {
|
||
|
decodeAsRenderedImage(page++);
|
||
|
} catch(IOException e) {
|
||
|
// Ignore
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return maxPage + 1;
|
||
|
}
|
||
|
|
||
|
public synchronized RenderedImage decodeAsRenderedImage(int page)
|
||
|
throws IOException {
|
||
|
|
||
|
// Verify that the index is in range.
|
||
|
if (page < 0 || (maxPageFound && page > maxPage)) {
|
||
|
throw new IOException(JaiI18N.getString("GIFImageDecoder1"));
|
||
|
}
|
||
|
|
||
|
// Attempt to get the image from the cache.
|
||
|
Integer pageKey = new Integer(page);
|
||
|
if(images.containsKey(pageKey)) {
|
||
|
return (RenderedImage)images.get(pageKey);
|
||
|
}
|
||
|
|
||
|
// If the zeroth image, set the global color table.
|
||
|
if(prevPage == -1) {
|
||
|
try {
|
||
|
globalColorTable = readHeader(input);
|
||
|
} catch(IOException e) {
|
||
|
maxPageFound = true;
|
||
|
maxPage = -1;
|
||
|
throw e;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Force previous data to be read.
|
||
|
if(page > 0) {
|
||
|
for(int idx = prevSyncedPage + 1; idx < page; idx++) {
|
||
|
RenderedImage im =
|
||
|
(RenderedImage)images.get(new Integer(idx));
|
||
|
im.getTile(0, 0);
|
||
|
prevSyncedPage = idx;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Read as many images as possible.
|
||
|
RenderedImage image = null;
|
||
|
while(prevPage < page) {
|
||
|
int index = prevPage + 1;
|
||
|
RenderedImage ri = null;
|
||
|
try {
|
||
|
ri = new GIFImage(input, globalColorTable);
|
||
|
images.put(new Integer(index), ri);
|
||
|
if(index < page) {
|
||
|
ri.getTile(0, 0);
|
||
|
prevSyncedPage = index;
|
||
|
}
|
||
|
prevPage = index;
|
||
|
if(index == page) {
|
||
|
image = ri;
|
||
|
break;
|
||
|
}
|
||
|
} catch(IOException e) {
|
||
|
maxPageFound = true;
|
||
|
maxPage = prevPage;
|
||
|
throw e;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return image;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*/
|
||
|
class GIFImage extends SimpleRenderedImage {
|
||
|
// Constants used to control interlacing.
|
||
|
private static final int[] INTERLACE_INCREMENT = { 8, 8, 4, 2, -1 };
|
||
|
private static final int[] INTERLACE_OFFSET = { 0, 4, 2, 1, -1 };
|
||
|
|
||
|
// The source stream.
|
||
|
private SeekableStream input;
|
||
|
|
||
|
// The interlacing flag.
|
||
|
private boolean interlaceFlag = false;
|
||
|
|
||
|
// Variables used by LZW decoding
|
||
|
private byte[] block = new byte[255];
|
||
|
private int blockLength = 0;
|
||
|
private int bitPos = 0;
|
||
|
private int nextByte = 0;
|
||
|
private int initCodeSize;
|
||
|
private int clearCode;
|
||
|
private int eofCode;
|
||
|
|
||
|
// 32-bit lookahead buffer
|
||
|
private int next32Bits = 0;
|
||
|
|
||
|
// True if the end of the data blocks has been found,
|
||
|
// and we are simply draining the 32-bit buffer
|
||
|
private boolean lastBlockFound = false;
|
||
|
|
||
|
// The current interlacing pass, starting with 0.
|
||
|
private int interlacePass = 0;
|
||
|
|
||
|
// The image's tile.
|
||
|
private WritableRaster theTile = null;
|
||
|
|
||
|
// Read blocks of 1-255 bytes, stop at a 0-length block
|
||
|
private void skipBlocks() throws IOException {
|
||
|
while (true) {
|
||
|
int length = input.readUnsignedByte();
|
||
|
if (length == 0) {
|
||
|
break;
|
||
|
}
|
||
|
input.skipBytes(length);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new <code>GIFImage</code>. The input stream must
|
||
|
* be positioned at the start of the image, i.e., not at the
|
||
|
* start of the overall stream.
|
||
|
*
|
||
|
* @param input the stream from which to read.
|
||
|
* @param globalColorTable the global colormap of <code>null</code>.
|
||
|
*
|
||
|
* @throws IOException.
|
||
|
*/
|
||
|
GIFImage(SeekableStream input,
|
||
|
byte[] globalColorTable) throws IOException {
|
||
|
this.input = input;
|
||
|
|
||
|
byte[] localColorTable = null;
|
||
|
boolean transparentColorFlag = false;
|
||
|
int transparentColorIndex = 0;
|
||
|
|
||
|
// Read the image header initializing the local color table,
|
||
|
// if any, and the transparent index, if any.
|
||
|
|
||
|
try {
|
||
|
long startPosition = input.getFilePointer();
|
||
|
while (true) {
|
||
|
int blockType = input.readUnsignedByte();
|
||
|
if (blockType == 0x2c) { // Image Descriptor
|
||
|
// Skip image top and left position.
|
||
|
input.skipBytes(4);
|
||
|
|
||
|
width = input.readUnsignedShortLE();
|
||
|
height = input.readUnsignedShortLE();
|
||
|
|
||
|
int idPackedFields = input.readUnsignedByte();
|
||
|
boolean localColorTableFlag =
|
||
|
(idPackedFields & 0x80) != 0;
|
||
|
interlaceFlag = (idPackedFields & 0x40) != 0;
|
||
|
int numLCTEntries = 1 << ((idPackedFields & 0x7) + 1);
|
||
|
|
||
|
if (localColorTableFlag) {
|
||
|
// Read color table if any
|
||
|
localColorTable =
|
||
|
new byte[3*numLCTEntries];
|
||
|
input.readFully(localColorTable);
|
||
|
} else {
|
||
|
localColorTable = null;
|
||
|
}
|
||
|
|
||
|
// Now positioned at start of LZW-compressed pixels
|
||
|
break;
|
||
|
} else if (blockType == 0x21) { // Extension block
|
||
|
int label = input.readUnsignedByte();
|
||
|
|
||
|
if (label == 0xf9) { // Graphics Control Extension
|
||
|
input.read(); // extension length
|
||
|
int gcePackedFields = input.readUnsignedByte();
|
||
|
transparentColorFlag =
|
||
|
(gcePackedFields & 0x1) != 0;
|
||
|
|
||
|
input.skipBytes(2); // delay time
|
||
|
|
||
|
transparentColorIndex
|
||
|
= input.readUnsignedByte();
|
||
|
|
||
|
input.read(); // terminator
|
||
|
} else if (label == 0x1) { // Plain text extension
|
||
|
// Skip content.
|
||
|
input.skipBytes(13);
|
||
|
// Read but do not save content.
|
||
|
skipBlocks();
|
||
|
} else if (label == 0xfe) { // Comment extension
|
||
|
// Read but do not save content.
|
||
|
skipBlocks();
|
||
|
} else if (label == 0xff) { // Application extension
|
||
|
// Skip content.
|
||
|
input.skipBytes(12);
|
||
|
// Read but do not save content.
|
||
|
skipBlocks();
|
||
|
} else {
|
||
|
// Skip over unknown extension blocks
|
||
|
int length = 0;
|
||
|
do {
|
||
|
length = input.readUnsignedByte();
|
||
|
input.skipBytes(length);
|
||
|
} while (length > 0);
|
||
|
}
|
||
|
} else {
|
||
|
throw new IOException(JaiI18N.getString("GIFImage0")+" "+
|
||
|
blockType + "!");
|
||
|
}
|
||
|
}
|
||
|
} catch (IOException ioe) {
|
||
|
throw new IOException(JaiI18N.getString("GIFImage1"));
|
||
|
}
|
||
|
|
||
|
// Set the image layout from the header information.
|
||
|
|
||
|
// Set the image and tile grid origin to (0, 0).
|
||
|
minX = minY = tileGridXOffset = tileGridYOffset = 0;
|
||
|
|
||
|
// Force the image to have a single tile.
|
||
|
tileWidth = width;
|
||
|
tileHeight = height;
|
||
|
|
||
|
byte[] colorTable;
|
||
|
if (localColorTable != null) {
|
||
|
colorTable = localColorTable;
|
||
|
} else {
|
||
|
colorTable = 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 (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(transparentColorIndex,
|
||
|
lutLength - 1);
|
||
|
a[idx] = (byte)0;
|
||
|
}
|
||
|
|
||
|
int[] bitsPerSample = new int[1];
|
||
|
bitsPerSample[0] = bits;
|
||
|
|
||
|
if (a == null) {
|
||
|
colorModel = new IndexColorModel(bits, r.length, r, g, b);
|
||
|
} else {
|
||
|
colorModel = new IndexColorModel(bits, r.length, r, g, b, a);
|
||
|
}
|
||
|
|
||
|
sampleModel =
|
||
|
new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
|
||
|
width, height,
|
||
|
1, width,
|
||
|
new int[] {0});
|
||
|
}
|
||
|
|
||
|
// BEGIN LZW CODE
|
||
|
|
||
|
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.
|
||
|
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 = input.readUnsignedByte();
|
||
|
if (blockLength == 0) {
|
||
|
lastBlockFound = true;
|
||
|
return code;
|
||
|
} else {
|
||
|
int left = blockLength;
|
||
|
int off = 0;
|
||
|
while (left > 0) {
|
||
|
int nbytes = input.read(block, off, left);
|
||
|
off += nbytes;
|
||
|
left -= nbytes;
|
||
|
}
|
||
|
nextByte = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
next32Bits |= block[nextByte++] << 24;
|
||
|
}
|
||
|
|
||
|
return code;
|
||
|
}
|
||
|
|
||
|
private 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private Point outputPixels(byte[] string,
|
||
|
int len,
|
||
|
Point streamPos,
|
||
|
byte[] rowBuf) {
|
||
|
if (interlacePass < 0 || interlacePass > 3) {
|
||
|
return streamPos;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < len; i++) {
|
||
|
if (streamPos.x >= minX) {
|
||
|
rowBuf[streamPos.x - minX] = string[i];
|
||
|
}
|
||
|
|
||
|
// Process end-of-row
|
||
|
++streamPos.x;
|
||
|
if (streamPos.x == width) {
|
||
|
theTile.setDataElements(minX, streamPos.y, width, 1, rowBuf);
|
||
|
|
||
|
streamPos.x = 0;
|
||
|
if (interlaceFlag) {
|
||
|
streamPos.y += INTERLACE_INCREMENT[interlacePass];
|
||
|
if (streamPos.y >= height) {
|
||
|
++interlacePass;
|
||
|
if (interlacePass > 3) {
|
||
|
return streamPos;
|
||
|
}
|
||
|
streamPos.y = INTERLACE_OFFSET[interlacePass];
|
||
|
}
|
||
|
} else {
|
||
|
++streamPos.y;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return streamPos;
|
||
|
}
|
||
|
|
||
|
// END LZW CODE
|
||
|
|
||
|
public synchronized Raster getTile(int tileX, int tileY) {
|
||
|
|
||
|
// Should be a unique tile.
|
||
|
if (tileX != 0 || tileY != 0) {
|
||
|
throw new IllegalArgumentException(JaiI18N.getString("GIFImage2"));
|
||
|
}
|
||
|
|
||
|
// Return the tile if it's already computed.
|
||
|
if (theTile != null) {
|
||
|
return theTile;
|
||
|
}
|
||
|
|
||
|
// Initialize the destination image
|
||
|
theTile =
|
||
|
WritableRaster.createWritableRaster(sampleModel,
|
||
|
sampleModel.createDataBuffer(),
|
||
|
null);
|
||
|
|
||
|
// Position in stream coordinates.
|
||
|
Point streamPos = new Point(0, 0);
|
||
|
|
||
|
// Allocate a row of memory.
|
||
|
byte[] rowBuf = new byte[width];
|
||
|
|
||
|
try {
|
||
|
// Read and decode the image data, fill in theTile.
|
||
|
this.initCodeSize = input.readUnsignedByte();
|
||
|
|
||
|
// Read first data block
|
||
|
this.blockLength = input.readUnsignedByte();
|
||
|
int left = blockLength;
|
||
|
int off = 0;
|
||
|
while (left > 0) {
|
||
|
int nbytes = input.read(block, off, left);
|
||
|
left -= nbytes;
|
||
|
off += nbytes;
|
||
|
}
|
||
|
|
||
|
this.bitPos = 0;
|
||
|
this.nextByte = 0;
|
||
|
this.lastBlockFound = false;
|
||
|
|
||
|
// 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 (true) {
|
||
|
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) {
|
||
|
return theTile;
|
||
|
}
|
||
|
} else if (code == eofCode) {
|
||
|
return theTile;
|
||
|
} else {
|
||
|
int newSuffixIndex;
|
||
|
if (code < tableIndex) {
|
||
|
newSuffixIndex = code;
|
||
|
} else { // code == tableIndex
|
||
|
newSuffixIndex = oldCode;
|
||
|
}
|
||
|
|
||
|
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, streamPos, rowBuf);
|
||
|
oldCode = code;
|
||
|
}
|
||
|
} catch (IOException e) {
|
||
|
throw new RuntimeException(JaiI18N.getString("GIFImage3"));
|
||
|
}
|
||
|
}
|
||
|
}
|