| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.swt.internal.image; |
| |
| import java.io.*; |
| |
| public class PngLzBlockReader { |
| boolean isLastBlock; |
| byte compressionType; |
| int uncompressedBytesRemaining; |
| PngDecodingDataStream stream; |
| PngHuffmanTables huffmanTables; |
| |
| byte[] window; |
| int windowIndex; |
| int copyIndex; |
| int copyBytesRemaining; |
| |
| static final int UNCOMPRESSED = 0; |
| static final int COMPRESSED_FIXED = 1; |
| static final int COMPRESSED_DYNAMIC = 2; |
| |
| static final int END_OF_COMPRESSED_BLOCK = 256; |
| static final int FIRST_LENGTH_CODE = 257; |
| static final int LAST_LENGTH_CODE = 285; |
| static final int FIRST_DISTANCE_CODE = 1; |
| static final int LAST_DISTANCE_CODE = 29; |
| static final int FIRST_CODE_LENGTH_CODE = 4; |
| static final int LAST_CODE_LENGTH_CODE = 19; |
| |
| static final int[] lengthBases = { |
| 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, |
| 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 |
| } ; |
| static final int[] extraLengthBits = { |
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, |
| 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, |
| }; |
| static final int[] distanceBases = { |
| 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, |
| 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, |
| 6145, 8193, 12289, 16385, 24577, |
| }; |
| static final int[] extraDistanceBits = { |
| 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, |
| 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, |
| }; |
| |
| |
| PngLzBlockReader(PngDecodingDataStream stream) { |
| this.stream = stream; |
| isLastBlock = false; |
| } |
| |
| void setWindowSize(int windowSize) { |
| window = new byte[windowSize]; |
| } |
| |
| void readNextBlockHeader() throws IOException { |
| isLastBlock = stream.getNextIdatBit() != 0; |
| compressionType = (byte)(stream.getNextIdatBits(2) & 0xFF); |
| if (compressionType > 2) stream.error(); |
| |
| if (compressionType == UNCOMPRESSED) { |
| byte b1 = stream.getNextIdatByte(); |
| byte b2 = stream.getNextIdatByte(); |
| byte b3 = stream.getNextIdatByte(); |
| byte b4 = stream.getNextIdatByte(); |
| if (b1 != ~b3 || b2 != ~b4) stream.error(); |
| uncompressedBytesRemaining = (b1 & 0xFF) | ((b2 & 0xFF) << 8); |
| } else if (compressionType == COMPRESSED_DYNAMIC) { |
| huffmanTables = PngHuffmanTables.getDynamicTables(stream); |
| } else { |
| huffmanTables = PngHuffmanTables.getFixedTables(); |
| } |
| } |
| |
| byte getNextByte() throws IOException { |
| if (compressionType == UNCOMPRESSED) { |
| if (uncompressedBytesRemaining == 0) { |
| readNextBlockHeader(); |
| return getNextByte(); |
| } |
| uncompressedBytesRemaining--; |
| return stream.getNextIdatByte(); |
| } else { |
| byte value = getNextCompressedByte(); |
| if (value == END_OF_COMPRESSED_BLOCK) { |
| if (isLastBlock) stream.error(); |
| readNextBlockHeader(); |
| return getNextByte(); |
| } else { |
| return value; |
| } |
| } |
| } |
| |
| private void assertBlockAtEnd() throws IOException { |
| if (compressionType == UNCOMPRESSED) { |
| if (uncompressedBytesRemaining > 0) stream.error(); |
| } else if (copyBytesRemaining > 0 || |
| (huffmanTables.getNextLiteralValue(stream) != END_OF_COMPRESSED_BLOCK)) |
| { |
| stream.error(); |
| } |
| } |
| void assertCompressedDataAtEnd() throws IOException { |
| assertBlockAtEnd(); |
| while (!isLastBlock) { |
| readNextBlockHeader(); |
| assertBlockAtEnd(); |
| } |
| } |
| |
| private byte getNextCompressedByte() throws IOException { |
| if (copyBytesRemaining > 0) { |
| byte value = window[copyIndex]; |
| window[windowIndex] = value; |
| copyBytesRemaining--; |
| |
| copyIndex++; |
| windowIndex++; |
| if (copyIndex == window.length) copyIndex = 0; |
| if (windowIndex == window.length) windowIndex = 0; |
| |
| return value; |
| } |
| |
| int value = huffmanTables.getNextLiteralValue(stream); |
| if (value < END_OF_COMPRESSED_BLOCK) { |
| window[windowIndex] = (byte) (value & 0xFF); |
| windowIndex++; |
| if (windowIndex >= window.length) windowIndex = 0; |
| return (byte) (value & 0xFF); |
| } else if (value == END_OF_COMPRESSED_BLOCK) { |
| readNextBlockHeader(); |
| return getNextByte(); |
| } else if (value <= LAST_LENGTH_CODE) { |
| int extraBits = extraLengthBits[value - FIRST_LENGTH_CODE]; |
| int length = lengthBases[value - FIRST_LENGTH_CODE]; |
| if (extraBits > 0) { |
| length += stream.getNextIdatBits(extraBits); |
| } |
| |
| value = huffmanTables.getNextDistanceValue(stream); |
| if (value > LAST_DISTANCE_CODE) stream.error(); |
| extraBits = extraDistanceBits[value]; |
| int distance = distanceBases[value]; |
| if (extraBits > 0) { |
| distance += stream.getNextIdatBits(extraBits); |
| } |
| |
| copyIndex = windowIndex - distance; |
| if (copyIndex < 0) copyIndex += window.length; |
| |
| copyBytesRemaining = length; |
| return getNextCompressedByte(); |
| } else { |
| stream.error(); |
| return 0; |
| } |
| } |
| |
| } |