001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.swt.internal.image;
011:
012: import java.io.*;
013:
014: public class PngLzBlockReader {
015: boolean isLastBlock;
016: byte compressionType;
017: int uncompressedBytesRemaining;
018: PngDecodingDataStream stream;
019: PngHuffmanTables huffmanTables;
020:
021: byte[] window;
022: int windowIndex;
023: int copyIndex;
024: int copyBytesRemaining;
025:
026: static final int UNCOMPRESSED = 0;
027: static final int COMPRESSED_FIXED = 1;
028: static final int COMPRESSED_DYNAMIC = 2;
029:
030: static final int END_OF_COMPRESSED_BLOCK = 256;
031: static final int FIRST_LENGTH_CODE = 257;
032: static final int LAST_LENGTH_CODE = 285;
033: static final int FIRST_DISTANCE_CODE = 1;
034: static final int LAST_DISTANCE_CODE = 29;
035: static final int FIRST_CODE_LENGTH_CODE = 4;
036: static final int LAST_CODE_LENGTH_CODE = 19;
037:
038: static final int[] lengthBases = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13,
039: 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115,
040: 131, 163, 195, 227, 258 };
041: static final int[] extraLengthBits = { 0, 0, 0, 0, 0, 0, 0, 0, 1,
042: 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, };
043: static final int[] distanceBases = { 1, 2, 3, 4, 5, 7, 9, 13, 17,
044: 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025,
045: 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, };
046: static final int[] extraDistanceBits = { 0, 0, 0, 0, 1, 1, 2, 2, 3,
047: 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12,
048: 12, 13, 13, };
049:
050: PngLzBlockReader(PngDecodingDataStream stream) {
051: this .stream = stream;
052: isLastBlock = false;
053: }
054:
055: void setWindowSize(int windowSize) {
056: window = new byte[windowSize];
057: }
058:
059: void readNextBlockHeader() throws IOException {
060: isLastBlock = stream.getNextIdatBit() != 0;
061: compressionType = (byte) (stream.getNextIdatBits(2) & 0xFF);
062: if (compressionType > 2)
063: stream.error();
064:
065: if (compressionType == UNCOMPRESSED) {
066: byte b1 = stream.getNextIdatByte();
067: byte b2 = stream.getNextIdatByte();
068: byte b3 = stream.getNextIdatByte();
069: byte b4 = stream.getNextIdatByte();
070: if (b1 != ~b3 || b2 != ~b4)
071: stream.error();
072: uncompressedBytesRemaining = (b1 & 0xFF)
073: | ((b2 & 0xFF) << 8);
074: } else if (compressionType == COMPRESSED_DYNAMIC) {
075: huffmanTables = PngHuffmanTables.getDynamicTables(stream);
076: } else {
077: huffmanTables = PngHuffmanTables.getFixedTables();
078: }
079: }
080:
081: byte getNextByte() throws IOException {
082: if (compressionType == UNCOMPRESSED) {
083: if (uncompressedBytesRemaining == 0) {
084: readNextBlockHeader();
085: return getNextByte();
086: }
087: uncompressedBytesRemaining--;
088: return stream.getNextIdatByte();
089: } else {
090: byte value = getNextCompressedByte();
091: if (value == END_OF_COMPRESSED_BLOCK) {
092: if (isLastBlock)
093: stream.error();
094: readNextBlockHeader();
095: return getNextByte();
096: } else {
097: return value;
098: }
099: }
100: }
101:
102: private void assertBlockAtEnd() throws IOException {
103: if (compressionType == UNCOMPRESSED) {
104: if (uncompressedBytesRemaining > 0)
105: stream.error();
106: } else if (copyBytesRemaining > 0
107: || (huffmanTables.getNextLiteralValue(stream) != END_OF_COMPRESSED_BLOCK)) {
108: stream.error();
109: }
110: }
111:
112: void assertCompressedDataAtEnd() throws IOException {
113: assertBlockAtEnd();
114: while (!isLastBlock) {
115: readNextBlockHeader();
116: assertBlockAtEnd();
117: }
118: }
119:
120: private byte getNextCompressedByte() throws IOException {
121: if (copyBytesRemaining > 0) {
122: byte value = window[copyIndex];
123: window[windowIndex] = value;
124: copyBytesRemaining--;
125:
126: copyIndex++;
127: windowIndex++;
128: if (copyIndex == window.length)
129: copyIndex = 0;
130: if (windowIndex == window.length)
131: windowIndex = 0;
132:
133: return value;
134: }
135:
136: int value = huffmanTables.getNextLiteralValue(stream);
137: if (value < END_OF_COMPRESSED_BLOCK) {
138: window[windowIndex] = (byte) (value & 0xFF);
139: windowIndex++;
140: if (windowIndex >= window.length)
141: windowIndex = 0;
142: return (byte) (value & 0xFF);
143: } else if (value == END_OF_COMPRESSED_BLOCK) {
144: readNextBlockHeader();
145: return getNextByte();
146: } else if (value <= LAST_LENGTH_CODE) {
147: int extraBits = extraLengthBits[value - FIRST_LENGTH_CODE];
148: int length = lengthBases[value - FIRST_LENGTH_CODE];
149: if (extraBits > 0) {
150: length += stream.getNextIdatBits(extraBits);
151: }
152:
153: value = huffmanTables.getNextDistanceValue(stream);
154: if (value > LAST_DISTANCE_CODE)
155: stream.error();
156: extraBits = extraDistanceBits[value];
157: int distance = distanceBases[value];
158: if (extraBits > 0) {
159: distance += stream.getNextIdatBits(extraBits);
160: }
161:
162: copyIndex = windowIndex - distance;
163: if (copyIndex < 0)
164: copyIndex += window.length;
165:
166: copyBytesRemaining = length;
167: return getNextCompressedByte();
168: } else {
169: stream.error();
170: return 0;
171: }
172: }
173:
174: }
|