25
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2000, 2006 IBM Corporation and others.
|
|
3 * All rights reserved. This program and the accompanying materials
|
|
4 * are made available under the terms of the Eclipse Public License v1.0
|
|
5 * which accompanies this distribution, and is available at
|
|
6 * http://www.eclipse.org/legal/epl-v10.html
|
|
7 *
|
|
8 * Contributors:
|
|
9 * IBM Corporation - initial API and implementation
|
|
10 * Port to the D programming language:
|
|
11 * Frank Benoit <benoit@tionex.de>
|
|
12 *******************************************************************************/
|
|
13 module org.eclipse.swt.internal.image.PngLzBlockReader;
|
|
14
|
|
15 import java.lang.all;
|
|
16
|
|
17 import org.eclipse.swt.internal.image.PngDecodingDataStream;
|
|
18 import org.eclipse.swt.internal.image.PngHuffmanTables;
|
|
19
|
|
20 public class PngLzBlockReader {
|
|
21 bool isLastBlock;
|
|
22 byte compressionType;
|
|
23 int uncompressedBytesRemaining;
|
|
24 PngDecodingDataStream stream;
|
|
25 PngHuffmanTables huffmanTables;
|
|
26
|
|
27 byte[] window;
|
|
28 int windowIndex;
|
|
29 int copyIndex;
|
|
30 int copyBytesRemaining;
|
|
31
|
|
32 static const int UNCOMPRESSED = 0;
|
|
33 static const int COMPRESSED_FIXED = 1;
|
|
34 static const int COMPRESSED_DYNAMIC = 2;
|
|
35
|
|
36 static const int END_OF_COMPRESSED_BLOCK = 256;
|
|
37 static const int FIRST_LENGTH_CODE = 257;
|
|
38 static const int LAST_LENGTH_CODE = 285;
|
|
39 static const int FIRST_DISTANCE_CODE = 1;
|
|
40 static const int LAST_DISTANCE_CODE = 29;
|
|
41 static const int FIRST_CODE_LENGTH_CODE = 4;
|
|
42 static const int LAST_CODE_LENGTH_CODE = 19;
|
|
43
|
|
44 static const int[] lengthBases = [
|
|
45 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27,
|
|
46 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
|
|
47 ];
|
|
48 static const int[] extraLengthBits = [
|
|
49 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
|
|
50 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0,
|
|
51 ];
|
|
52 static const int[] distanceBases = [
|
|
53 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129,
|
|
54 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097,
|
|
55 6145, 8193, 12289, 16385, 24577,
|
|
56 ];
|
|
57 static const int[] extraDistanceBits = [
|
|
58 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
|
|
59 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
|
|
60 ];
|
|
61
|
|
62
|
|
63 this(PngDecodingDataStream stream) {
|
|
64 this.stream = stream;
|
|
65 isLastBlock = false;
|
|
66 }
|
|
67
|
|
68 void setWindowSize(int windowSize) {
|
|
69 window = new byte[windowSize];
|
|
70 }
|
|
71
|
|
72 void readNextBlockHeader() {
|
|
73 isLastBlock = stream.getNextIdatBit() !is 0;
|
|
74 compressionType = cast(byte)(stream.getNextIdatBits(2) & 0xFF);
|
|
75 if (compressionType > 2) stream.error();
|
|
76
|
|
77 if (compressionType is UNCOMPRESSED) {
|
|
78 byte b1 = stream.getNextIdatByte();
|
|
79 byte b2 = stream.getNextIdatByte();
|
|
80 byte b3 = stream.getNextIdatByte();
|
|
81 byte b4 = stream.getNextIdatByte();
|
|
82 if (b1 !is ~b3 || b2 !is ~b4) stream.error();
|
|
83 uncompressedBytesRemaining = (b1 & 0xFF) | ((b2 & 0xFF) << 8);
|
|
84 } else if (compressionType is COMPRESSED_DYNAMIC) {
|
|
85 huffmanTables = PngHuffmanTables.getDynamicTables(stream);
|
|
86 } else {
|
|
87 huffmanTables = PngHuffmanTables.getFixedTables();
|
|
88 }
|
|
89 }
|
|
90
|
|
91 byte getNextByte() {
|
|
92 if (compressionType is UNCOMPRESSED) {
|
|
93 if (uncompressedBytesRemaining is 0) {
|
|
94 readNextBlockHeader();
|
|
95 return getNextByte();
|
|
96 }
|
|
97 uncompressedBytesRemaining--;
|
|
98 return stream.getNextIdatByte();
|
|
99 } else {
|
|
100 byte value = getNextCompressedByte();
|
|
101 if (value is END_OF_COMPRESSED_BLOCK) {
|
|
102 if (isLastBlock) stream.error();
|
|
103 readNextBlockHeader();
|
|
104 return getNextByte();
|
|
105 } else {
|
|
106 return value;
|
|
107 }
|
|
108 }
|
|
109 }
|
|
110
|
|
111 private void assertBlockAtEnd() {
|
|
112 if (compressionType is UNCOMPRESSED) {
|
|
113 if (uncompressedBytesRemaining > 0) stream.error();
|
|
114 } else if (copyBytesRemaining > 0 ||
|
|
115 (huffmanTables.getNextLiteralValue(stream) !is END_OF_COMPRESSED_BLOCK))
|
|
116 {
|
|
117 stream.error();
|
|
118 }
|
|
119 }
|
|
120 void assertCompressedDataAtEnd() {
|
|
121 assertBlockAtEnd();
|
|
122 while (!isLastBlock) {
|
|
123 readNextBlockHeader();
|
|
124 assertBlockAtEnd();
|
|
125 }
|
|
126 }
|
|
127
|
|
128 private byte getNextCompressedByte() {
|
|
129 if (copyBytesRemaining > 0) {
|
|
130 byte value = window[copyIndex];
|
|
131 window[windowIndex] = value;
|
|
132 copyBytesRemaining--;
|
|
133
|
|
134 copyIndex++;
|
|
135 windowIndex++;
|
|
136 if (copyIndex is window.length) copyIndex = 0;
|
|
137 if (windowIndex is window.length) windowIndex = 0;
|
|
138
|
|
139 return value;
|
|
140 }
|
|
141
|
|
142 int value = huffmanTables.getNextLiteralValue(stream);
|
|
143 if (value < END_OF_COMPRESSED_BLOCK) {
|
|
144 window[windowIndex] = cast(byte) (value & 0xFF);
|
|
145 windowIndex++;
|
|
146 if (windowIndex >= window.length) windowIndex = 0;
|
|
147 return cast(byte) (value & 0xFF);
|
|
148 } else if (value is END_OF_COMPRESSED_BLOCK) {
|
|
149 readNextBlockHeader();
|
|
150 return getNextByte();
|
|
151 } else if (value <= LAST_LENGTH_CODE) {
|
|
152 int extraBits = extraLengthBits[value - FIRST_LENGTH_CODE];
|
|
153 int length = lengthBases[value - FIRST_LENGTH_CODE];
|
|
154 if (extraBits > 0) {
|
|
155 length += stream.getNextIdatBits(extraBits);
|
|
156 }
|
|
157
|
|
158 value = huffmanTables.getNextDistanceValue(stream);
|
|
159 if (value > LAST_DISTANCE_CODE) stream.error();
|
|
160 extraBits = extraDistanceBits[value];
|
|
161 int distance = distanceBases[value];
|
|
162 if (extraBits > 0) {
|
|
163 distance += stream.getNextIdatBits(extraBits);
|
|
164 }
|
|
165
|
|
166 copyIndex = windowIndex - distance;
|
|
167 if (copyIndex < 0) copyIndex += window.length;
|
|
168
|
|
169 copyBytesRemaining = length;
|
|
170 return getNextCompressedByte();
|
|
171 } else {
|
|
172 stream.error();
|
|
173 return 0;
|
|
174 }
|
|
175 }
|
|
176
|
|
177 }
|