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