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 + -
显示快捷键?