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 org.eclipse.swt.*;
013: import java.io.*;
014:
015: class PngChunk extends Object {
016: byte[] reference;
017:
018: static final int LENGTH_OFFSET = 0;
019: static final int TYPE_OFFSET = 4;
020: static final int DATA_OFFSET = 8;
021:
022: static final int TYPE_FIELD_LENGTH = 4;
023: static final int LENGTH_FIELD_LENGTH = 4;
024: static final int MIN_LENGTH = 12;
025:
026: static final int CHUNK_UNKNOWN = -1;
027: // Critical chunks.
028: static final int CHUNK_IHDR = 0;
029: static final int CHUNK_PLTE = 1;
030: static final int CHUNK_IDAT = 2;
031: static final int CHUNK_IEND = 3;
032: // Non-critical chunks.
033: static final int CHUNK_tRNS = 5;
034:
035: static final byte[] TYPE_IHDR = { (byte) 'I', (byte) 'H',
036: (byte) 'D', (byte) 'R' };
037: static final byte[] TYPE_PLTE = { (byte) 'P', (byte) 'L',
038: (byte) 'T', (byte) 'E' };
039: static final byte[] TYPE_IDAT = { (byte) 'I', (byte) 'D',
040: (byte) 'A', (byte) 'T' };
041: static final byte[] TYPE_IEND = { (byte) 'I', (byte) 'E',
042: (byte) 'N', (byte) 'D' };
043: static final byte[] TYPE_tRNS = { (byte) 't', (byte) 'R',
044: (byte) 'N', (byte) 'S' };
045:
046: static final int[] CRC_TABLE;
047: static {
048: CRC_TABLE = new int[256];
049: for (int i = 0; i < 256; i++) {
050: CRC_TABLE[i] = i;
051: for (int j = 0; j < 8; j++) {
052: if ((CRC_TABLE[i] & 0x1) == 0) {
053: CRC_TABLE[i] = (CRC_TABLE[i] >> 1) & 0x7FFFFFFF;
054: } else {
055: CRC_TABLE[i] = 0xEDB88320 ^ ((CRC_TABLE[i] >> 1) & 0x7FFFFFFF);
056: }
057: }
058: }
059: }
060:
061: int length;
062:
063: /**
064: * Construct a PngChunk using the reference bytes
065: * given.
066: */
067: PngChunk(byte[] reference) {
068: super ();
069: setReference(reference);
070: if (reference.length < LENGTH_OFFSET + LENGTH_FIELD_LENGTH)
071: SWT.error(SWT.ERROR_INVALID_IMAGE);
072: length = getInt32(LENGTH_OFFSET);
073: }
074:
075: /**
076: * Construct a PngChunk with the specified number of
077: * data bytes.
078: */
079: PngChunk(int dataLength) {
080: this (new byte[MIN_LENGTH + dataLength]);
081: setLength(dataLength);
082: }
083:
084: /**
085: * Get the PngChunk's reference byteArray;
086: */
087: byte[] getReference() {
088: return reference;
089: }
090:
091: /**
092: * Set the PngChunk's reference byteArray;
093: */
094: void setReference(byte[] reference) {
095: this .reference = reference;
096: }
097:
098: /**
099: * Get the 16-bit integer from the reference byte
100: * array at the given offset.
101: */
102: int getInt16(int offset) {
103: int answer = 0;
104: answer |= (reference[offset] & 0xFF) << 8;
105: answer |= (reference[offset + 1] & 0xFF);
106: return answer;
107: }
108:
109: /**
110: * Set the 16-bit integer in the reference byte
111: * array at the given offset.
112: */
113: void setInt16(int offset, int value) {
114: reference[offset] = (byte) ((value >> 8) & 0xFF);
115: reference[offset + 1] = (byte) (value & 0xFF);
116: }
117:
118: /**
119: * Get the 32-bit integer from the reference byte
120: * array at the given offset.
121: */
122: int getInt32(int offset) {
123: int answer = 0;
124: answer |= (reference[offset] & 0xFF) << 24;
125: answer |= (reference[offset + 1] & 0xFF) << 16;
126: answer |= (reference[offset + 2] & 0xFF) << 8;
127: answer |= (reference[offset + 3] & 0xFF);
128: return answer;
129: }
130:
131: /**
132: * Set the 32-bit integer in the reference byte
133: * array at the given offset.
134: */
135: void setInt32(int offset, int value) {
136: reference[offset] = (byte) ((value >> 24) & 0xFF);
137: reference[offset + 1] = (byte) ((value >> 16) & 0xFF);
138: reference[offset + 2] = (byte) ((value >> 8) & 0xFF);
139: reference[offset + 3] = (byte) (value & 0xFF);
140: }
141:
142: /**
143: * Get the length of the data component of this chunk.
144: * This is not the length of the entire chunk.
145: */
146: int getLength() {
147: return length;
148: }
149:
150: /**
151: * Set the length of the data component of this chunk.
152: * This is not the length of the entire chunk.
153: */
154: void setLength(int value) {
155: setInt32(LENGTH_OFFSET, value);
156: length = value;
157: }
158:
159: /**
160: * Get the chunk type. This is a four byte value.
161: * Each byte should be an ASCII character.
162: * The first byte is upper case if the chunk is critical.
163: * The second byte is upper case if the chunk is publicly defined.
164: * The third byte must be upper case.
165: * The fourth byte is upper case if the chunk is unsafe to copy.
166: * Public chunk types are defined by the PNG Development Group.
167: */
168: byte[] getTypeBytes() {
169: byte[] type = new byte[4];
170: System.arraycopy(reference, TYPE_OFFSET, type, 0,
171: TYPE_FIELD_LENGTH);
172: return type;
173: }
174:
175: /**
176: * Set the chunk type. This is a four byte value.
177: * Each byte should be an ASCII character.
178: * The first byte is upper case if the chunk is critical.
179: * The second byte is upper case if the chunk is publicly defined.
180: * The third byte must be upper case.
181: * The fourth byte is upper case if the chunk is unsafe to copy.
182: * Public chunk types are defined by the PNG Development Group.
183: */
184: void setType(byte[] value) {
185: if (value.length != TYPE_FIELD_LENGTH) {
186: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
187: }
188: System.arraycopy(value, 0, reference, TYPE_OFFSET,
189: TYPE_FIELD_LENGTH);
190: }
191:
192: /**
193: * Get the chunk's data.
194: */
195: byte[] getData() {
196: int dataLength = getLength();
197: if (reference.length < MIN_LENGTH + dataLength) {
198: SWT.error(SWT.ERROR_INVALID_RANGE);
199: }
200: byte[] data = new byte[dataLength];
201: System.arraycopy(reference, DATA_OFFSET, data, 0, dataLength);
202: return data;
203: }
204:
205: /**
206: * Set the chunk's data.
207: * This method has two side-effects.
208: * 1. It will set the length field to be the length
209: * of the data array given.
210: * 2. It will set the CRC field to the computed CRC
211: * value of the data array given.
212: */
213: void setData(byte[] data) {
214: setLength(data.length);
215: System.arraycopy(data, 0, reference, DATA_OFFSET, data.length);
216: setCRC(computeCRC());
217: }
218:
219: /**
220: * Get the CRC value for the chunk's data.
221: * Ensure that the length field has a good
222: * value before making this call.
223: */
224: int getCRC() {
225: int crcOffset = DATA_OFFSET + getLength();
226: return getInt32(crcOffset);
227: }
228:
229: /**
230: * Set the CRC value for the chunk's data.
231: * Ensure that the length field has a good
232: * value before making this call.
233: */
234: void setCRC(int value) {
235: int crcOffset = DATA_OFFSET + getLength();
236: setInt32(crcOffset, value);
237: }
238:
239: /**
240: * Get the chunk's total size including the length, type, and crc fields.
241: */
242: int getSize() {
243: return MIN_LENGTH + getLength();
244: }
245:
246: /**
247: * Compute the CRC value for the chunk's data. Answer
248: * whether this value matches the value stored in the
249: * chunk.
250: */
251: boolean checkCRC() {
252: int crc = computeCRC();
253: int storedCRC = getCRC();
254: return crc == storedCRC;
255: }
256:
257: /**
258: * Answer the CRC value of chunk's data.
259: */
260: int computeCRC() {
261: int crc = 0xFFFFFFFF;
262: int start = TYPE_OFFSET;
263: int stop = DATA_OFFSET + getLength();
264: for (int i = start; i < stop; i++) {
265: int index = (crc ^ reference[i]) & 0xFF;
266: crc = CRC_TABLE[index] ^ ((crc >> 8) & 0x00FFFFFF);
267: }
268: return ~crc;
269: }
270:
271: boolean typeMatchesArray(byte[] array) {
272: for (int i = 0; i < TYPE_FIELD_LENGTH; i++) {
273: if (reference[TYPE_OFFSET + i] != array[i]) {
274: return false;
275: }
276: }
277: return true;
278: }
279:
280: boolean isCritical() {
281: char c = (char) getTypeBytes()[0];
282: return 'A' <= c && c <= 'Z';
283: }
284:
285: int getChunkType() {
286: if (typeMatchesArray(TYPE_IHDR))
287: return CHUNK_IHDR;
288: if (typeMatchesArray(TYPE_PLTE))
289: return CHUNK_PLTE;
290: if (typeMatchesArray(TYPE_IDAT))
291: return CHUNK_IDAT;
292: if (typeMatchesArray(TYPE_IEND))
293: return CHUNK_IEND;
294: if (typeMatchesArray(TYPE_tRNS))
295: return CHUNK_tRNS;
296: return CHUNK_UNKNOWN;
297: }
298:
299: /**
300: * Read the next PNG chunk from the input stream given.
301: * If unable to read a chunk, return null.
302: */
303: static PngChunk readNextFromStream(LEDataInputStream stream) {
304: try {
305: int headerLength = LENGTH_FIELD_LENGTH + TYPE_FIELD_LENGTH;
306: byte[] headerBytes = new byte[headerLength];
307: int result = stream.read(headerBytes, 0, headerLength);
308: stream.unread(headerBytes);
309: if (result != headerLength)
310: return null;
311:
312: PngChunk tempChunk = new PngChunk(headerBytes);
313:
314: int chunkLength = tempChunk.getSize();
315: byte[] chunk = new byte[chunkLength];
316: result = stream.read(chunk, 0, chunkLength);
317: if (result != chunkLength)
318: return null;
319:
320: switch (tempChunk.getChunkType()) {
321: case CHUNK_IHDR:
322: return new PngIhdrChunk(chunk);
323: case CHUNK_PLTE:
324: return new PngPlteChunk(chunk);
325: case CHUNK_IDAT:
326: return new PngIdatChunk(chunk);
327: case CHUNK_IEND:
328: return new PngIendChunk(chunk);
329: case CHUNK_tRNS:
330: return new PngTrnsChunk(chunk);
331: default:
332: return new PngChunk(chunk);
333: }
334: } catch (IOException e) {
335: return null;
336: }
337: }
338:
339: /**
340: * Answer whether the chunk is a valid PNG chunk.
341: */
342: void validate(PngFileReadState readState, PngIhdrChunk headerChunk) {
343: if (reference.length < MIN_LENGTH)
344: SWT.error(SWT.ERROR_INVALID_IMAGE);
345:
346: byte[] type = getTypeBytes();
347:
348: // The third character MUST be upper case.
349: char c = (char) type[2];
350: if (!('A' <= c && c <= 'Z'))
351: SWT.error(SWT.ERROR_INVALID_IMAGE);
352:
353: // All characters must be letters.
354: for (int i = 0; i < TYPE_FIELD_LENGTH; i++) {
355: c = (char) type[i];
356: if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))) {
357: SWT.error(SWT.ERROR_INVALID_IMAGE);
358: }
359: }
360:
361: // The stored CRC must match the data's computed CRC.
362: if (!checkCRC())
363: SWT.error(SWT.ERROR_INVALID_IMAGE);
364: }
365:
366: /**
367: * Provided so that subclasses can override and add
368: * data to the toString() call.
369: */
370: void contributeToString(StringBuffer buffer) {
371: }
372:
373: /**
374: * Returns a string containing a concise, human-readable
375: * description of the receiver.
376: *
377: * @return a string representation of the event
378: */
379: public String toString() {
380: StringBuffer buffer = new StringBuffer();
381: buffer.append("{");
382: buffer.append("\n\tLength: ");
383: buffer.append(getLength());
384: buffer.append("\n\tType: ");
385: byte[] type = getTypeBytes();
386: for (int i = 0; i < type.length; i++) {
387: buffer.append((char) type[i]);
388: }
389:
390: contributeToString(buffer);
391:
392: buffer.append("\n\tCRC: ");
393: buffer.append(Integer.toHexString(getCRC()));
394: buffer.append("\n}");
395: return buffer.toString();
396: }
397:
398: }
|