gifdecoder.java
来自「纯java操作系统jnode,安装简单和操作简单的个人使用的Java操作系统」· Java 代码 · 共 673 行 · 第 1/2 页
JAVA
673 行
/*
* $Id: GIFDecoder.java,v 1.1 2003/11/25 11:51:39 epr Exp $
*/
package org.jnode.awt.image;
import java.awt.image.ColorModel;
import java.awt.image.ImageConsumer;
import java.awt.image.ImageProducer;
import java.awt.image.IndexColorModel;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
/**
* @author epr
*/
public class GIFDecoder implements ImageProducer {
private final LinkedList consumers = new LinkedList();
private byte bytePixels[]; // image data in index model
private int intPixels[]; // image data in RGB model
private int width; // image size
private int height;
ColorModel colorModel;
private int imageStatus;
//private String fullInfo; // Format: field in info box
//private String shortInfo; // short format info
private String comment; // comment text
private InputStream input;
private boolean closeWhenFinished;
/**
* Creates an instance of a GIF decoder for further reading from <code>input</code>. Image
* reading from the stream starts only at <code>startProduction ()</code> or <code>addConsumer ()</code>
* call. By default, <code>input</code> isn't closed once image reading is done.
*
* @param input
*/
public GIFDecoder(InputStream input) {
this(input, false);
}
/**
* Creates an instance of a GIF decoder for further reading from <code>input</code>. Image
* reading from the stream starts only at <code>startProduction ()</code> or <code>addConsumer ()</code>
* call.
*
* @param input
* an input stream
* @param closeWhenFinished
* if <code>true</code> then <code>input</code> will be closed once image
* reading will be done.
* @since PJA2.4
*/
public GIFDecoder(InputStream input, boolean closeWhenFinished) {
this.input = input;
this.closeWhenFinished = closeWhenFinished;
}
/**
* <code>ImageProducer</code> implementation.
*
* @param ic
*/
public void startProduction(ImageConsumer ic) {
addConsumer(ic);
}
/**
* <code>ImageProducer</code> implementation.
*
* @param ic
*/
public void addConsumer(ImageConsumer ic) {
if (ic != null && isConsumer(ic))
return;
synchronized (this) {
if (imageStatus == 0)
try {
loadGIF(input);
imageStatus = ImageConsumer.STATICIMAGEDONE;
} catch (IOException e) {
imageStatus = ImageConsumer.IMAGEERROR;
}
}
if (imageStatus != ImageConsumer.IMAGEERROR) {
ic.setDimensions(width, height);
ic.setHints(ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME | ImageConsumer.TOPDOWNLEFTRIGHT);
if (colorModel != null) {
ic.setColorModel(colorModel);
ic.setPixels(0, 0, width, height, colorModel, bytePixels, 0, width);
} else
// If colorModel can't be instantiated send image in default RGB format
ic.setPixels(0, 0, width, height, null, intPixels, 0, width);
ic.imageComplete(imageStatus);
} else
ic.imageComplete(imageStatus);
}
/**
* <code>ImageProducer</code> implementation.
*
* @param ic
* @return True or false
*/
public boolean isConsumer(ImageConsumer ic) {
return consumers.contains(ic);
}
/**
* <code>ImageProducer</code> implementation.
*
* @param ic
*/
public void removeConsumer(ImageConsumer ic) {
consumers.remove(ic);
}
/**
* <code>ImageProducer</code> implementation.
*
* @param ic
*/
public void requestTopDownLeftRightResend(ImageConsumer ic) {
// Useless, already sent in that order
}
// xvgif.c translation starts here
// Transformed most of global variables to local variables
// These variables are used only once when image is loaded
// (loadGIF () is called in a synchronized block)
private int bitOffset = 0; // Bit Offset of next code
private int XC = 0; // Output X and Y coords of current pixel
private int YC = 0;
private int pass = 0; // Used by output routine if interlaced pixels
private int ptr = 0;
private int oldYC = -1;
private byte r[] = new byte[256];
private byte g[] = new byte[256];
private byte b[] = new byte[256]; // colormap, if PIC8
private int transparentIndex = -1;
private final static String id87 = "GIF87a";
private final static String id89 = "GIF89a";
private final static short EGA_PALETTE[][] = { { 0, 0, 0 }, {
0, 0, 128 }, {
0, 128, 0 }, {
0, 128, 128 }, {
128, 0, 0 }, {
128, 0, 128 }, {
128, 128, 0 }, {
200, 200, 200 }, {
100, 100, 100 }, {
100, 100, 255 }, {
100, 255, 100 }, {
100, 255, 255 }, {
255, 100, 100 }, {
255, 100, 255 }, {
255, 255, 100 }, {
255, 255, 255 }
};
private final static byte EXTENSION = 0x21;
private final static byte IMAGESEP = 0x2c;
private final static byte TRAILER = 0x3b;
private final static byte INTERLACEMASK = 0x40;
private final static byte COLORMAPMASK = (byte) 0x80;
private void loadGIF(InputStream input) throws IOException {
try {
if (!(input instanceof BufferedInputStream))
input = new BufferedInputStream(input);
// Use a DataInputStream to have EOFException if file is corrupted
DataInputStream dataInput = new DataInputStream(input);
// initialize variables
bitOffset = XC = YC = pass = 0;
boolean gotimage = false;
boolean gif89 = false;
byte[] idBytes = new byte[6];
for (int i = 0; i < 6; i++)
idBytes[i] = dataInput.readByte();
final String id = new String(idBytes);
if (id.equals(id87)) {
gif89 = false;
} else if (id.equals(id89)) {
gif89 = true;
} else {
gifWarning(input, "not a GIF file");
}
// Get variables from the GIF screen descriptor
dataInput.skipBytes(4);
//byte aByte = dataInput.readByte();
//int screenWidth = (aByte & 0xFF) + 0x100 * (dataInput.readByte() & 0xFF); // screen
// dimensions... not used.
//aByte = dataInput.readByte();
//int screenHeight = (aByte & 0xFF) + 0x100 * (dataInput.readByte() & 0xFF);
byte aByte = dataInput.readByte();
boolean hasColormap = (aByte & COLORMAPMASK) != 0;
// Bits per pixel, read from GIF header
int bitsPerPixel = (aByte & 7) + 1;
// Number of colors
int colorMapSize = 1 << bitsPerPixel;
// AND mask for data size
int bitMask = colorMapSize - 1;
dataInput.skipBytes(1);
//int background = dataInput.readByte() & 0xFF; // background color... not used.
int aspect = dataInput.readByte() & 0xFF;
if (aspect != 0)
if (!gif89)
gifWarning(input, "corrupt GIF file (screen descriptor)");
// Read in global colormap.
if (hasColormap)
for (int i = 0; i < colorMapSize; i++) {
r[i] = dataInput.readByte();
g[i] = dataInput.readByte();
b[i] = dataInput.readByte();
} else
// no colormap in GIF file
// put std EGA palette (repeated 16 times) into colormap, for lack of
// anything better to do
for (int i = 0; i < 256; i++) {
r[i] = (byte) EGA_PALETTE[i & 15][0];
g[i] = (byte) EGA_PALETTE[i & 15][1];
b[i] = (byte) EGA_PALETTE[i & 15][2];
}
for (int block = 0;(block = dataInput.readByte() & 0xFF) != TRAILER;)
if (block == EXTENSION) {
// possible things at this point are:
// an application extension block
// a comment extension block
// an (optional) graphic control extension block
// followed by either an image
// or a plaintext extension
// parse extension blocks
int fn, blocksize, aspnum, aspden;
// read extension block
fn = dataInput.readByte() & 0xFF;
if (fn == 'R') {
// GIF87 aspect extension
int blockSize;
blocksize = dataInput.readByte() & 0xFF;
if (blocksize == 2) {
aspnum = dataInput.readByte();
aspden = dataInput.readByte();
if (aspden <= 0 || aspnum <= 0) {
aspnum = aspden = 1;
}
} else
dataInput.skipBytes(blocksize);
while ((blockSize = dataInput.readByte() & 0xFF) > 0)
// eat any following data subblocks
dataInput.skipBytes(blockSize);
} else if (fn == 0xFE) {
// Comment Extension
for (int blockSize = 0;(blockSize = dataInput.readByte() & 0xFF) != 0;) {
byte commentBytes[] = new byte[blockSize];
for (int i = 0; i < blockSize; i++)
commentBytes[i] = dataInput.readByte();
if (comment != null) {
comment += "\n" + new String(commentBytes);
} else {
comment = new String(commentBytes);
}
}
} else if (fn == 0x01) {
// PlainText Extension
int blockSize = dataInput.readByte() & 0xFF;
int tgLeft = dataInput.readByte() & 0xFF;
tgLeft += (dataInput.readByte() & 0xFF) << 8;
int tgTop = dataInput.readByte() & 0xFF;
tgTop += (dataInput.readByte() & 0xFF) << 8;
int tgWidth = dataInput.readByte() & 0xFF;
tgWidth += (dataInput.readByte() & 0xFF) << 8;
int tgHeight = dataInput.readByte() & 0xFF;
tgHeight += (dataInput.readByte() & 0xFF) << 8;
dataInput.skipBytes(4);
/*
* int cWidth = dataInput.readByte() & 0xFF; int cHeight =
* dataInput.readByte() & 0xFF; int fg = dataInput.readByte() & 0xFF;
*/
dataInput.skipBytes(blockSize - 12); // read rest of first subblock
// read (and ignore) data sub-blocks
while ((blockSize = dataInput.readByte() & 0xFF) != 0)
dataInput.skipBytes(blockSize);
} else if (fn == 0xF9) {
// Graphic Control Extension
for (int blockSize = 0;(blockSize = dataInput.readByte() & 0xFF) != 0;)
// Added transparent GIF management here
if (blockSize == 4) {
int ext1 = (dataInput.readByte() & 0xFF);
dataInput.skipBytes(2);
int ext4 = (dataInput.readByte() & 0xFF);
// v2.1.1 Changed condition for transparent GIFs
if ((ext1 & 0x01) != 0)
transparentIndex = ext4;
} else
dataInput.skipBytes(blockSize);
} else if (fn == 0xFF) {
// Application Extension
// read (and ignore) data sub-blocks
for (int blockSize = 0;(blockSize = dataInput.readByte() & 0xFF) != 0;)
dataInput.skipBytes(blockSize);
} else {
// unknown extension
// read (and ignore) data sub-blocks
for (int blockSize = 0;(blockSize = dataInput.readByte() & 0xFF) != 0;)
dataInput.skipBytes(blockSize);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?