blob: 3aed7038bd2f99bef4fe22c81554b8921eb37471 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2011 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);
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 {
return getNextCompressedByte();
}
}
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;
windowIndex++;
if (windowIndex >= window.length) windowIndex = 0;
return (byte) value;
} 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;
}
}
}