comparison dwt/internal/image/PngChunk.d @ 0:380af2bdd8e5

Upload of whole dwt tree
author Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com>
date Sat, 09 Aug 2008 17:00:02 +0200
parents
children 1a8b3cb347e0
comparison
equal deleted inserted replaced
-1:000000000000 0:380af2bdd8e5
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 *******************************************************************************/
11 module dwt.internal.image;
12
13
14 import java.io.IOException;
15
16 import dwt.DWT;
17
18 class PngChunk : Object {
19 byte[] reference;
20
21 static final int LENGTH_OFFSET = 0;
22 static final int TYPE_OFFSET = 4;
23 static final int DATA_OFFSET = 8;
24
25 static final int TYPE_FIELD_LENGTH = 4;
26 static final int LENGTH_FIELD_LENGTH = 4;
27 static final int MIN_LENGTH = 12;
28
29 static final int CHUNK_UNKNOWN = -1;
30 // Critical chunks.
31 static final int CHUNK_IHDR = 0;
32 static final int CHUNK_PLTE = 1;
33 static final int CHUNK_IDAT = 2;
34 static final int CHUNK_IEND = 3;
35 // Non-critical chunks.
36 static final int CHUNK_tRNS = 5;
37
38 static final byte[] TYPE_IHDR = {(byte) 'I', (byte) 'H', (byte) 'D', (byte) 'R'};
39 static final byte[] TYPE_PLTE = {(byte) 'P', (byte) 'L', (byte) 'T', (byte) 'E'};
40 static final byte[] TYPE_IDAT = {(byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T'};
41 static final byte[] TYPE_IEND = {(byte) 'I', (byte) 'E', (byte) 'N', (byte) 'D'};
42 static final byte[] TYPE_tRNS = {(byte) 't', (byte) 'R', (byte) 'N', (byte) 'S'};
43
44 static final int[] CRC_TABLE;
45 static {
46 CRC_TABLE = new int[256];
47 for (int i = 0; i < 256; i++) {
48 CRC_TABLE[i] = i;
49 for (int j = 0; j < 8; j++) {
50 if ((CRC_TABLE[i] & 0x1) is 0) {
51 CRC_TABLE[i] = (CRC_TABLE[i] >> 1) & 0x7FFFFFFF;
52 } else {
53 CRC_TABLE[i] = 0xEDB88320 ^ ((CRC_TABLE[i] >> 1) & 0x7FFFFFFF);
54 }
55 }
56 }
57 }
58
59 int length;
60
61 /**
62 * Construct a PngChunk using the reference bytes
63 * given.
64 */
65 PngChunk(byte[] reference) {
66 super();
67 setReference(reference);
68 if (reference.length < LENGTH_OFFSET + LENGTH_FIELD_LENGTH) DWT.error(DWT.ERROR_INVALID_IMAGE);
69 length = getInt32(LENGTH_OFFSET);
70 }
71
72 /**
73 * Construct a PngChunk with the specified number of
74 * data bytes.
75 */
76 PngChunk(int dataLength) {
77 this(new byte[MIN_LENGTH + dataLength]);
78 setLength(dataLength);
79 }
80
81 /**
82 * Get the PngChunk's reference byteArray;
83 */
84 byte[] getReference() {
85 return reference;
86 }
87
88 /**
89 * Set the PngChunk's reference byteArray;
90 */
91 void setReference(byte[] reference) {
92 this.reference = reference;
93 }
94
95 /**
96 * Get the 16-bit integer from the reference byte
97 * array at the given offset.
98 */
99 int getInt16(int offset) {
100 int answer = 0;
101 answer |= (reference[offset] & 0xFF) << 8;
102 answer |= (reference[offset + 1] & 0xFF);
103 return answer;
104 }
105
106 /**
107 * Set the 16-bit integer in the reference byte
108 * array at the given offset.
109 */
110 void setInt16(int offset, int value) {
111 reference[offset] = (byte) ((value >> 8) & 0xFF);
112 reference[offset + 1] = (byte) (value & 0xFF);
113 }
114
115 /**
116 * Get the 32-bit integer from the reference byte
117 * array at the given offset.
118 */
119 int getInt32(int offset) {
120 int answer = 0;
121 answer |= (reference[offset] & 0xFF) << 24;
122 answer |= (reference[offset + 1] & 0xFF) << 16;
123 answer |= (reference[offset + 2] & 0xFF) << 8;
124 answer |= (reference[offset + 3] & 0xFF);
125 return answer;
126 }
127
128 /**
129 * Set the 32-bit integer in the reference byte
130 * array at the given offset.
131 */
132 void setInt32(int offset, int value) {
133 reference[offset] = (byte) ((value >> 24) & 0xFF);
134 reference[offset + 1] = (byte) ((value >> 16) & 0xFF);
135 reference[offset + 2] = (byte) ((value >> 8) & 0xFF);
136 reference[offset + 3] = (byte) (value & 0xFF);
137 }
138
139 /**
140 * Get the length of the data component of this chunk.
141 * This is not the length of the entire chunk.
142 */
143 int getLength() {
144 return length;
145 }
146
147 /**
148 * Set the length of the data component of this chunk.
149 * This is not the length of the entire chunk.
150 */
151 void setLength(int value) {
152 setInt32(LENGTH_OFFSET, value);
153 length = value;
154 }
155
156 /**
157 * Get the chunk type. This is a four byte value.
158 * Each byte should be an ASCII character.
159 * The first byte is upper case if the chunk is critical.
160 * The second byte is upper case if the chunk is publicly defined.
161 * The third byte must be upper case.
162 * The fourth byte is upper case if the chunk is unsafe to copy.
163 * Public chunk types are defined by the PNG Development Group.
164 */
165 byte[] getTypeBytes() {
166 byte[] type = new byte[4];
167 System.arraycopy(reference, TYPE_OFFSET, type, 0, TYPE_FIELD_LENGTH);
168 return type;
169 }
170
171 /**
172 * Set the chunk type. This is a four byte value.
173 * Each byte should be an ASCII character.
174 * The first byte is upper case if the chunk is critical.
175 * The second byte is upper case if the chunk is publicly defined.
176 * The third byte must be upper case.
177 * The fourth byte is upper case if the chunk is unsafe to copy.
178 * Public chunk types are defined by the PNG Development Group.
179 */
180 void setType(byte[] value) {
181 if (value.length !is TYPE_FIELD_LENGTH) {
182 DWT.error (DWT.ERROR_INVALID_ARGUMENT);
183 }
184 System.arraycopy(value, 0, reference, TYPE_OFFSET, TYPE_FIELD_LENGTH);
185 }
186
187 /**
188 * Get the chunk's data.
189 */
190 byte[] getData() {
191 int dataLength = getLength();
192 if (reference.length < MIN_LENGTH + dataLength) {
193 DWT.error (DWT.ERROR_INVALID_RANGE);
194 }
195 byte[] data = new byte[dataLength];
196 System.arraycopy(reference, DATA_OFFSET, data, 0, dataLength);
197 return data;
198 }
199
200 /**
201 * Set the chunk's data.
202 * This method has two side-effects.
203 * 1. It will set the length field to be the length
204 * of the data array given.
205 * 2. It will set the CRC field to the computed CRC
206 * value of the data array given.
207 */
208 void setData(byte[] data) {
209 setLength(data.length);
210 System.arraycopy(data, 0, reference, DATA_OFFSET, data.length);
211 setCRC(computeCRC());
212 }
213
214 /**
215 * Get the CRC value for the chunk's data.
216 * Ensure that the length field has a good
217 * value before making this call.
218 */
219 int getCRC() {
220 int crcOffset = DATA_OFFSET + getLength();
221 return getInt32(crcOffset);
222 }
223
224 /**
225 * Set the CRC value for the chunk's data.
226 * Ensure that the length field has a good
227 * value before making this call.
228 */
229 void setCRC(int value) {
230 int crcOffset = DATA_OFFSET + getLength();
231 setInt32(crcOffset, value);
232 }
233
234 /**
235 * Get the chunk's total size including the length, type, and crc fields.
236 */
237 int getSize() {
238 return MIN_LENGTH + getLength();
239 }
240
241 /**
242 * Compute the CRC value for the chunk's data. Answer
243 * whether this value matches the value stored in the
244 * chunk.
245 */
246 bool checkCRC() {
247 int crc = computeCRC();
248 int storedCRC = getCRC();
249 return crc is storedCRC;
250 }
251
252 /**
253 * Answer the CRC value of chunk's data.
254 */
255 int computeCRC() {
256 int crc = 0xFFFFFFFF;
257 int start = TYPE_OFFSET;
258 int stop = DATA_OFFSET + getLength();
259 for (int i = start; i < stop; i++) {
260 int index = (crc ^ reference[i]) & 0xFF;
261 crc = CRC_TABLE[index] ^ ((crc >> 8) & 0x00FFFFFF);
262 }
263 return ~crc;
264 }
265
266 bool typeMatchesArray(byte[] array) {
267 for (int i = 0; i < TYPE_FIELD_LENGTH; i++) {
268 if (reference[TYPE_OFFSET + i] !is array[i]){
269 return false;
270 }
271 }
272 return true;
273 }
274
275 bool isCritical() {
276 char c = (char) getTypeBytes()[0];
277 return 'A' <= c && c <= 'Z';
278 }
279
280 int getChunkType() {
281 if (typeMatchesArray(TYPE_IHDR)) return CHUNK_IHDR;
282 if (typeMatchesArray(TYPE_PLTE)) return CHUNK_PLTE;
283 if (typeMatchesArray(TYPE_IDAT)) return CHUNK_IDAT;
284 if (typeMatchesArray(TYPE_IEND)) return CHUNK_IEND;
285 if (typeMatchesArray(TYPE_tRNS)) return CHUNK_tRNS;
286 return CHUNK_UNKNOWN;
287 }
288
289 /**
290 * Read the next PNG chunk from the input stream given.
291 * If unable to read a chunk, return null.
292 */
293 static PngChunk readNextFromStream(LEDataInputStream stream) {
294 try {
295 int headerLength = LENGTH_FIELD_LENGTH + TYPE_FIELD_LENGTH;
296 byte[] headerBytes = new byte[headerLength];
297 int result = stream.read(headerBytes, 0, headerLength);
298 stream.unread(headerBytes);
299 if (result !is headerLength) return null;
300
301 PngChunk tempChunk = new PngChunk(headerBytes);
302
303 int chunkLength = tempChunk.getSize();
304 byte[] chunk = new byte[chunkLength];
305 result = stream.read(chunk, 0, chunkLength);
306 if (result !is chunkLength) return null;
307
308 switch (tempChunk.getChunkType()) {
309 case CHUNK_IHDR:
310 return new PngIhdrChunk(chunk);
311 case CHUNK_PLTE:
312 return new PngPlteChunk(chunk);
313 case CHUNK_IDAT:
314 return new PngIdatChunk(chunk);
315 case CHUNK_IEND:
316 return new PngIendChunk(chunk);
317 case CHUNK_tRNS:
318 return new PngTrnsChunk(chunk);
319 default:
320 return new PngChunk(chunk);
321 }
322 } catch (IOException e) {
323 return null;
324 }
325 }
326
327 /**
328 * Answer whether the chunk is a valid PNG chunk.
329 */
330 void validate(PngFileReadState readState, PngIhdrChunk headerChunk) {
331 if (reference.length < MIN_LENGTH) DWT.error(DWT.ERROR_INVALID_IMAGE);
332
333 byte[] type = getTypeBytes();
334
335 // The third character MUST be upper case.
336 char c = (char) type[2];
337 if (!('A' <= c && c <= 'Z')) DWT.error(DWT.ERROR_INVALID_IMAGE);
338
339 // All characters must be letters.
340 for (int i = 0; i < TYPE_FIELD_LENGTH; i++) {
341 c = (char) type[i];
342 if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))) {
343 DWT.error(DWT.ERROR_INVALID_IMAGE);
344 }
345 }
346
347 // The stored CRC must match the data's computed CRC.
348 if (!checkCRC()) DWT.error(DWT.ERROR_INVALID_IMAGE);
349 }
350
351 /**
352 * Provided so that subclasses can override and add
353 * data to the toString() call.
354 */
355 void contributeToString(StringBuffer buffer) {}
356
357 /**
358 * Returns a String containing a concise, human-readable
359 * description of the receiver.
360 *
361 * @return a String representation of the event
362 */
363 public String toString() {
364 StringBuffer buffer = new StringBuffer();
365 buffer.append("{");
366 buffer.append("\n\tLength: ");
367 buffer.append(getLength());
368 buffer.append("\n\tType: ");
369 byte[] type = getTypeBytes();
370 for(int i = 0; i < type.length; i++) {
371 buffer.append((char) type[i]);
372 }
373
374 contributeToString(buffer);
375
376 buffer.append("\n\tCRC: ");
377 buffer.append(Integer.toHexString(getCRC()));
378 buffer.append("\n}");
379 return buffer.toString();
380 }
381
382 }