root/dwt/internal/image/PngChunk.d

Revision 213:36f5cb12e1a2, 10.9 kB (checked in by Frank Benoit <benoit@tionex.de>, 8 months ago)

Update to SWT 3.4M7

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