KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > swt > internal > image > PngChunk


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 package org.eclipse.swt.internal.image;
12
13
14 import org.eclipse.swt.*;
15 import java.io.*;
16
17 class PngChunk extends Object JavaDoc {
18     byte[] reference;
19
20     static final int LENGTH_OFFSET = 0;
21     static final int TYPE_OFFSET = 4;
22     static final int DATA_OFFSET = 8;
23
24     static final int TYPE_FIELD_LENGTH = 4;
25     static final int LENGTH_FIELD_LENGTH = 4;
26     static final int MIN_LENGTH = 12;
27
28     static final int CHUNK_UNKNOWN = -1;
29     // Critical chunks.
30
static final int CHUNK_IHDR = 0;
31     static final int CHUNK_PLTE = 1;
32     static final int CHUNK_IDAT = 2;
33     static final int CHUNK_IEND = 3;
34     // Non-critical chunks.
35
static final int CHUNK_tRNS = 5;
36     
37     static final byte[] TYPE_IHDR = {(byte) 'I', (byte) 'H', (byte) 'D', (byte) 'R'};
38     static final byte[] TYPE_PLTE = {(byte) 'P', (byte) 'L', (byte) 'T', (byte) 'E'};
39     static final byte[] TYPE_IDAT = {(byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T'};
40     static final byte[] TYPE_IEND = {(byte) 'I', (byte) 'E', (byte) 'N', (byte) 'D'};
41     static final byte[] TYPE_tRNS = {(byte) 't', (byte) 'R', (byte) 'N', (byte) 'S'};
42     
43     static final int[] CRC_TABLE;
44     static {
45         CRC_TABLE = new int[256];
46         for (int i = 0; i < 256; i++) {
47             CRC_TABLE[i] = i;
48             for (int j = 0; j < 8; j++) {
49                 if ((CRC_TABLE[i] & 0x1) == 0) {
50                     CRC_TABLE[i] = (CRC_TABLE[i] >> 1) & 0x7FFFFFFF;
51                 } else {
52                     CRC_TABLE[i] = 0xEDB88320 ^ ((CRC_TABLE[i] >> 1) & 0x7FFFFFFF);
53                 }
54             }
55         }
56     }
57     
58     int length;
59     
60 /**
61  * Construct a PngChunk using the reference bytes
62  * given.
63  */

64 PngChunk(byte[] reference) {
65     super();
66     setReference(reference);
67     if (reference.length < LENGTH_OFFSET + LENGTH_FIELD_LENGTH) SWT.error(SWT.ERROR_INVALID_IMAGE);
68     length = getInt32(LENGTH_OFFSET);
69 }
70
71 /**
72  * Construct a PngChunk with the specified number of
73  * data bytes.
74  */

75 PngChunk(int dataLength) {
76     this(new byte[MIN_LENGTH + dataLength]);
77     setLength(dataLength);
78 }
79
80 /**
81  * Get the PngChunk's reference byteArray;
82  */

83 byte[] getReference() {
84     return reference;
85 }
86
87 /**
88  * Set the PngChunk's reference byteArray;
89  */

90 void setReference(byte[] reference) {
91     this.reference = reference;
92 }
93
94 /**
95  * Get the 16-bit integer from the reference byte
96  * array at the given offset.
97  */

98 int getInt16(int offset) {
99     int answer = 0;
100     answer |= (reference[offset] & 0xFF) << 8;
101     answer |= (reference[offset + 1] & 0xFF);
102     return answer;
103 }
104
105 /**
106  * Set the 16-bit integer in the reference byte
107  * array at the given offset.
108  */

109 void setInt16(int offset, int value) {
110     reference[offset] = (byte) ((value >> 8) & 0xFF);
111     reference[offset + 1] = (byte) (value & 0xFF);
112 }
113
114 /**
115  * Get the 32-bit integer from the reference byte
116  * array at the given offset.
117  */

118 int getInt32(int offset) {
119     int answer = 0;
120     answer |= (reference[offset] & 0xFF) << 24;
121     answer |= (reference[offset + 1] & 0xFF) << 16;
122     answer |= (reference[offset + 2] & 0xFF) << 8;
123     answer |= (reference[offset + 3] & 0xFF);
124     return answer;
125 }
126
127 /**
128  * Set the 32-bit integer in the reference byte
129  * array at the given offset.
130  */

131 void setInt32(int offset, int value) {
132     reference[offset] = (byte) ((value >> 24) & 0xFF);
133     reference[offset + 1] = (byte) ((value >> 16) & 0xFF);
134     reference[offset + 2] = (byte) ((value >> 8) & 0xFF);
135     reference[offset + 3] = (byte) (value & 0xFF);
136 }
137
138 /**
139  * Get the length of the data component of this chunk.
140  * This is not the length of the entire chunk.
141  */

142 int getLength() {
143     return length;
144 }
145
146 /**
147  * Set the length of the data component of this chunk.
148  * This is not the length of the entire chunk.
149  */

150 void setLength(int value) {
151     setInt32(LENGTH_OFFSET, value);
152     length = value;
153 }
154
155 /**
156  * Get the chunk type. This is a four byte value.
157  * Each byte should be an ASCII character.
158  * The first byte is upper case if the chunk is critical.
159  * The second byte is upper case if the chunk is publicly defined.
160  * The third byte must be upper case.
161  * The fourth byte is upper case if the chunk is unsafe to copy.
162  * Public chunk types are defined by the PNG Development Group.
163  */

164 byte[] getTypeBytes() {
165     byte[] type = new byte[4];
166     System.arraycopy(reference, TYPE_OFFSET, type, 0, TYPE_FIELD_LENGTH);
167     return type;
168 }
169
170 /**
171  * Set the chunk type. This is a four byte value.
172  * Each byte should be an ASCII character.
173  * The first byte is upper case if the chunk is critical.
174  * The second byte is upper case if the chunk is publicly defined.
175  * The third byte must be upper case.
176  * The fourth byte is upper case if the chunk is unsafe to copy.
177  * Public chunk types are defined by the PNG Development Group.
178  */

179 void setType(byte[] value) {
180     if (value.length != TYPE_FIELD_LENGTH) {
181         SWT.error (SWT.ERROR_INVALID_ARGUMENT);
182     }
183     System.arraycopy(value, 0, reference, TYPE_OFFSET, TYPE_FIELD_LENGTH);
184 }
185
186 /**
187  * Get the chunk's data.
188  */

189 byte[] getData() {
190     int dataLength = getLength();
191     if (reference.length < MIN_LENGTH + dataLength) {
192         SWT.error (SWT.ERROR_INVALID_RANGE);
193     }
194     byte[] data = new byte[dataLength];
195     System.arraycopy(reference, DATA_OFFSET, data, 0, dataLength);
196     return data;
197 }
198
199 /**
200  * Set the chunk's data.
201  * This method has two side-effects.
202  * 1. It will set the length field to be the length
203  * of the data array given.
204  * 2. It will set the CRC field to the computed CRC
205  * value of the data array given.
206  */

207 void setData(byte[] data) {
208     setLength(data.length);
209     System.arraycopy(data, 0, reference, DATA_OFFSET, data.length);
210     setCRC(computeCRC());
211 }
212
213 /**
214  * Get the CRC value for the chunk's data.
215  * Ensure that the length field has a good
216  * value before making this call.
217  */

218 int getCRC() {
219     int crcOffset = DATA_OFFSET + getLength();
220     return getInt32(crcOffset);
221 }
222
223 /**
224  * Set the CRC value for the chunk's data.
225  * Ensure that the length field has a good
226  * value before making this call.
227  */

228 void setCRC(int value) {
229     int crcOffset = DATA_OFFSET + getLength();
230     setInt32(crcOffset, value);
231 }
232
233 /**
234  * Get the chunk's total size including the length, type, and crc fields.
235  */

236 int getSize() {
237     return MIN_LENGTH + getLength();
238 }
239
240 /**
241  * Compute the CRC value for the chunk's data. Answer
242  * whether this value matches the value stored in the
243  * chunk.
244  */

245 boolean checkCRC() {
246     int crc = computeCRC();
247     int storedCRC = getCRC();
248     return crc == storedCRC;
249 }
250
251 /**
252  * Answer the CRC value of chunk's data.
253  */

254 int computeCRC() {
255     int crc = 0xFFFFFFFF;
256     int start = TYPE_OFFSET;
257     int stop = DATA_OFFSET + getLength();
258     for (int i = start; i < stop; i++) {
259         int index = (crc ^ reference[i]) & 0xFF;
260         crc = CRC_TABLE[index] ^ ((crc >> 8) & 0x00FFFFFF);
261     }
262     return ~crc;
263 }
264
265 boolean typeMatchesArray(byte[] array) {
266     for (int i = 0; i < TYPE_FIELD_LENGTH; i++) {
267         if (reference[TYPE_OFFSET + i] != array[i]){
268             return false;
269         }
270     }
271     return true;
272 }
273
274 boolean isCritical() {
275     char c = (char) getTypeBytes()[0];
276     return 'A' <= c && c <= 'Z';
277 }
278
279 int getChunkType() {
280     if (typeMatchesArray(TYPE_IHDR)) return CHUNK_IHDR;
281     if (typeMatchesArray(TYPE_PLTE)) return CHUNK_PLTE;
282     if (typeMatchesArray(TYPE_IDAT)) return CHUNK_IDAT;
283     if (typeMatchesArray(TYPE_IEND)) return CHUNK_IEND;
284     if (typeMatchesArray(TYPE_tRNS)) return CHUNK_tRNS;
285     return CHUNK_UNKNOWN;
286 }
287
288 /**
289  * Read the next PNG chunk from the input stream given.
290  * If unable to read a chunk, return null.
291  */

292 static PngChunk readNextFromStream(LEDataInputStream stream) {
293     try {
294         int headerLength = LENGTH_FIELD_LENGTH + TYPE_FIELD_LENGTH;
295         byte[] headerBytes = new byte[headerLength];
296         int result = stream.read(headerBytes, 0, headerLength);
297         stream.unread(headerBytes);
298         if (result != headerLength) return null;
299         
300         PngChunk tempChunk = new PngChunk(headerBytes);
301         
302         int chunkLength = tempChunk.getSize();
303         byte[] chunk = new byte[chunkLength];
304         result = stream.read(chunk, 0, chunkLength);
305         if (result != chunkLength) return null;
306     
307         switch (tempChunk.getChunkType()) {
308             case CHUNK_IHDR:
309                 return new PngIhdrChunk(chunk);
310             case CHUNK_PLTE:
311                 return new PngPlteChunk(chunk);
312             case CHUNK_IDAT:
313                 return new PngIdatChunk(chunk);
314             case CHUNK_IEND:
315                 return new PngIendChunk(chunk);
316             case CHUNK_tRNS:
317                 return new PngTrnsChunk(chunk);
318             default:
319                 return new PngChunk(chunk);
320         }
321     } catch (IOException e) {
322         return null;
323     }
324 }
325
326 /**
327  * Answer whether the chunk is a valid PNG chunk.
328  */

329 void validate(PngFileReadState readState, PngIhdrChunk headerChunk) {
330     if (reference.length < MIN_LENGTH) SWT.error(SWT.ERROR_INVALID_IMAGE);
331     
332     byte[] type = getTypeBytes();
333     
334     // The third character MUST be upper case.
335
char c = (char) type[2];
336     if (!('A' <= c && c <= 'Z')) SWT.error(SWT.ERROR_INVALID_IMAGE);
337     
338     // All characters must be letters.
339
for (int i = 0; i < TYPE_FIELD_LENGTH; i++) {
340         c = (char) type[i];
341         if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))) {
342             SWT.error(SWT.ERROR_INVALID_IMAGE);
343         }
344     }
345     
346     // The stored CRC must match the data's computed CRC.
347
if (!checkCRC()) SWT.error(SWT.ERROR_INVALID_IMAGE);
348 }
349
350 /**
351  * Provided so that subclasses can override and add
352  * data to the toString() call.
353  */

354 void contributeToString(StringBuffer JavaDoc buffer) {}
355
356 /**
357  * Returns a string containing a concise, human-readable
358  * description of the receiver.
359  *
360  * @return a string representation of the event
361  */

362 public String JavaDoc toString() {
363     StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
364     buffer.append("{");
365     buffer.append("\n\tLength: ");
366     buffer.append(getLength());
367     buffer.append("\n\tType: ");
368     byte[] type = getTypeBytes();
369     for(int i = 0; i < type.length; i++) {
370         buffer.append((char) type[i]);
371     }
372     
373     contributeToString(buffer);
374     
375     buffer.append("\n\tCRC: ");
376     buffer.append(Integer.toHexString(getCRC()));
377     buffer.append("\n}");
378     return buffer.toString();
379 }
380
381 }
382
Popular Tags