gifdecoder.java
来自「纯java操作系统jnode,安装简单和操作简单的个人使用的Java操作系统」· Java 代码 · 共 673 行 · 第 1/2 页
JAVA
673 行
}
} else if (block == IMAGESEP) {
if (gotimage) {
// just skip over remaining images
dataInput.skipBytes(8); // left position
// top position
// width
// height
int misc = dataInput.readByte() & 0xFF; // misc. bits
if ((misc & 0x80) != 0)
// image has local colormap. skip it
for (int i = 0; i < 1 << ((misc & 7) + 1); i++)
dataInput.skipBytes(3);
dataInput.skipBytes(1); // minimum code size
// skip image data sub-blocks
for (int blockSize = 0;(blockSize = dataInput.readByte() & 0xFF) != 0;)
dataInput.skipBytes(blockSize);
} else {
readImage(dataInput, bitsPerPixel, bitMask, hasColormap, gif89);
gotimage = true;
}
} else {
// unknown block type
// don't mention bad block if file was trunc'd, as it's all bogus
String str = "Unknown block type (0x" + Integer.toString(block, 16) + ")";
gifWarning(input, str);
break;
}
if (!gotimage)
gifWarning(input, "no image data found in GIF file");
} finally {
// v2.4 image InputStream close when finished
if (closeWhenFinished)
try {
input.close();
input = null;
} catch (IOException e) {
throw e;
}
}
}
private void readImage(DataInputStream dataInput, int bitsPerPixel, int bitMask, boolean hasColormap, boolean gif89) throws IOException {
int npixels = 0;
int maxpixels = 0;
// read in values from the image descriptor
byte aByte;
dataInput.skipBytes(4);
//aByte = dataInput.readByte();
//int leftOffset = (aByte & 0xFF) + 0x100 * (dataInput.readByte() & 0xFF);
//aByte = dataInput.readByte();
//int topOffset = (aByte & 0xFF) + 0x100 * (dataInput.readByte() & 0xFF);
aByte = dataInput.readByte();
width = (aByte & 0xFF) + 0x100 * (dataInput.readByte() & 0xFF);
aByte = dataInput.readByte();
height = (aByte & 0xFF) + 0x100 * (dataInput.readByte() & 0xFF);
int misc = dataInput.readByte(); // miscellaneous bits (interlace, local cmap)
boolean interlace = (misc & INTERLACEMASK) != 0;
if ((misc & 0x80) != 0)
for (int i = 0; i < 1 << ((misc & 7) + 1); i++) {
r[i] = dataInput.readByte();
g[i] = dataInput.readByte();
b[i] = dataInput.readByte();
}
if (!hasColormap && (misc & 0x80) == 0) {
// no global or local colormap
}
// Start reading the raster data. First we get the intial code size
// and compute decompressor constant values, based on this code size.
// Code size, read from GIF header
int codeSize = dataInput.readByte() & 0xFF;
int clearCode = (1 << codeSize); // GIF clear code
int EOFCode = clearCode + 1; // GIF end-of-information code
int firstFree = clearCode + 2; // First free code, generated per GIF spec
int freeCode = firstFree; // Decompressor,next free slot in hash table
// The GIF spec has it that the code size is the code size used to
// compute the above values is the code size given in the file, but the
// code size used in compression/decompression is the code size given in
// the file plus one. (thus the ++).
codeSize++;
int initCodeSize = codeSize; // Starting code size, used during Clear
int maxCode = (1 << codeSize); // limiting value for current code size
int readMask = maxCode - 1; // Code AND mask for current code size
// UNBLOCK:
// Read the raster data. Here we just transpose it from the GIF array
// to the raster array, turning it from a series of blocks into one long
// data stream, which makes life much easier for readCode ().
byte[] raster = null;
for (int blockSize = 0;(blockSize = dataInput.readByte() & 0xFF) != 0;) {
int start = 0;
if (raster == null)
raster = new byte[blockSize];
else {
byte oldData[] = raster;
raster = new byte[oldData.length + blockSize];
System.arraycopy(oldData, 0, raster, 0, oldData.length);
start = oldData.length;
}
while (blockSize-- > 0)
raster[start++] = dataInput.readByte();
}
// Allocate the 'pixels'
maxpixels = width * height;
bytePixels = new byte[maxpixels];
int picptr = 0;
// The hash table used by the decompressor
int prefix[] = new int[4096];
int suffix[] = new int[4096];
// An output array used by the decompressor
int outCode[] = new int[4097];
int outCount = 0; // Decompressor output 'stack count'
int currentCode; // Decompressor variables
int oldCode = 0;
int inCode;
int finChar = 0;
// Decompress the file, continuing until you see the GIF EOF code.
// One obvious enhancement is to add checking for corrupt files here.
int code = readCode(dataInput, raster, codeSize, readMask);
while (code != EOFCode) {
// Clear code sets everything back to its initial value, then reads the
// immediately subsequent code as uncompressed data.
if (code == clearCode) {
codeSize = initCodeSize;
maxCode = (1 << codeSize);
readMask = maxCode - 1;
freeCode = firstFree;
code = readCode(dataInput, raster, codeSize, readMask);
currentCode = oldCode = code;
finChar = currentCode & bitMask;
if (!interlace)
bytePixels[picptr++] = (byte) finChar;
else
doInterlace(finChar);
npixels++;
} else {
// If not a clear code, must be data: save same as currentCode and inCode
// if we're at maxcode and didn't get a clear, stop loading
if (freeCode >= 4096)
break;
currentCode = inCode = code;
// If greater or equal to freeCode, not in the hash table yet;
// repeat the last character decoded
if (currentCode >= freeCode) {
currentCode = oldCode;
if (outCount > 4096)
break;
outCode[outCount++] = finChar;
}
// Unless this code is raw data, pursue the chain pointed to by currentCode
// through the hash table to its end; each code in the chain puts its
// associated output code on the output queue.
while (currentCode > bitMask) {
if (outCount > 4096)
break; // corrupt file
outCode[outCount++] = suffix[currentCode];
currentCode = prefix[currentCode];
}
if (outCount > 4096)
break;
// The last code in the chain is treated as raw data.
finChar = currentCode & bitMask;
outCode[outCount++] = finChar;
// Now we put the data out to the Output routine.
// It's been stacked LIFO, so deal with it that way...
// safety thing: prevent exceeding range of 'bytePixels'
if (npixels + outCount > maxpixels)
outCount = maxpixels - npixels;
npixels += outCount;
if (!interlace)
for (int i = outCount - 1; i >= 0; i--)
bytePixels[picptr++] = (byte) outCode[i];
else
for (int i = outCount - 1; i >= 0; i--)
doInterlace(outCode[i]);
outCount = 0;
// Build the hash table on-the-fly. No table is stored in the file.
prefix[freeCode] = oldCode;
suffix[freeCode] = finChar;
oldCode = inCode;
// Point to the next slot in the table. If we exceed the current
// maxCode value, increment the code size unless it's already 12. If it
// is, do nothing: the next code decompressed better be CLEAR
freeCode++;
if (freeCode >= maxCode) {
if (codeSize < 12) {
codeSize++;
maxCode *= 2;
readMask = (1 << codeSize) - 1;
}
}
}
code = readCode(dataInput, raster, codeSize, readMask);
if (npixels >= maxpixels)
break;
}
if (npixels != maxpixels) {
if (!interlace) // clear.EOBuffer
for (int i = 0; i < maxpixels - npixels; i++)
bytePixels[npixels + i] = 0;
}
// fill in the GifImage structure
colorModel = new IndexColorModel(8, 256, r, g, b, transparentIndex);
//fullInfo = "GIF" + ((gif89) ? "89" : "87") + ", " + bitsPerPixel + " bit" +
// ((bitsPerPixel == 1) ? "" : "s") + "per pixel, " + (interlace ? "" : "non-") +
// "interlaced.";
//shortInfo = width + "x" + height + " GIF" + ((gif89) ? "89" : "87");
// comment gets handled in main LoadGIF() block-reader
}
/**
* Fetch the next code from the raster data stream. The codes can be any length from 3 to 12
* bits, packed into 8-bit bytes, so we have to maintain our location in the raster array as a
* BIT Offset. We compute the byte Offset into the raster array by dividing this by 8, pick up
* three bytes, compute the bit Offset into our 24-bit chunk, shift to bring the desired code
* to the bottom, then mask it off and return it.
*
* @param input
* @param raster
* @param codeSize
* @param readMask
* @return int
* @throws IOException
*/
private int readCode(DataInputStream input, byte raster[], int codeSize, int readMask) throws IOException {
int byteOffset = bitOffset / 8;
int inWordOffset = bitOffset % 8;
// v2.2
// Alan Dix modification to fix raster over-run errors
// int rawCode = (raster [byteOffset] & 0xFF)
// + ((raster [byteOffset + 1] & 0xFF) << 8);
int rawCode = (raster[byteOffset] & 0xFF);
if (byteOffset + 1 < raster.length)
rawCode += ((raster[byteOffset + 1] & 0xFF) << 8);
else if (codeSize + inWordOffset > 8)
gifWarning(input, "short raster ? raster.length = " + raster.length + ", codeSize = " + codeSize + ", readMask = " + readMask);
// end of modification
if (codeSize >= 8 && byteOffset + 2 < raster.length)
rawCode += (raster[byteOffset + 2] & 0xFF) << 16;
rawCode >>= (bitOffset % 8);
bitOffset += codeSize;
return rawCode & readMask;
}
private void doInterlace(int index) {
if (oldYC != YC) {
ptr = YC * width;
oldYC = YC;
}
if (YC < height)
bytePixels[ptr++] = (byte) index;
// Update the X-coordinate, and if it overflows, update the Y-coordinate
if (++XC == width) {
// deal with the interlace as described in the GIF
// spec. Put the decoded scan line out to the screen if we haven't gone
// past the bottom of it
XC = 0;
switch (pass) {
case 0 :
YC += 8;
if (YC >= height) {
pass++;
YC = 4;
}
break;
case 1 :
YC += 8;
if (YC >= height) {
pass++;
YC = 2;
}
break;
case 2 :
YC += 4;
if (YC >= height) {
pass++;
YC = 1;
}
break;
case 3 :
YC += 2;
break;
default :
break;
}
}
}
private void gifWarning(InputStream input, String st) throws IOException {
throw new IOException("Warning ! " + input + " : " + st);
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?