Mercurial > projects > dwt2
comparison org.eclipse.swt.win32.win32.x86/src/org/eclipse/swt/internal/image/TIFFDirectory.d @ 0:6dd524f61e62
add dwt win and basic java stuff
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 02 Mar 2009 14:44:16 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:6dd524f61e62 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2000, 2005 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.TIFFDirectory; | |
14 | |
15 import org.eclipse.swt.internal.image.TIFFRandomFileAccess; | |
16 import org.eclipse.swt.internal.image.TIFFModifiedHuffmanCodec; | |
17 import org.eclipse.swt.internal.image.LEDataOutputStream; | |
18 import org.eclipse.swt.graphics.ImageData; | |
19 import org.eclipse.swt.graphics.ImageLoaderEvent; | |
20 import org.eclipse.swt.graphics.ImageLoader; | |
21 import org.eclipse.swt.graphics.PaletteData; | |
22 import org.eclipse.swt.graphics.RGB; | |
23 import org.eclipse.swt.SWT; | |
24 import java.lang.all; | |
25 | |
26 final class TIFFDirectory { | |
27 | |
28 TIFFRandomFileAccess file; | |
29 bool isLittleEndian; | |
30 ImageLoader loader; | |
31 int depth; | |
32 | |
33 /* Directory fields */ | |
34 int imageWidth; | |
35 int imageLength; | |
36 int[] bitsPerSample; | |
37 int compression; | |
38 int photometricInterpretation; | |
39 int[] stripOffsets; | |
40 int samplesPerPixel; | |
41 int rowsPerStrip; | |
42 int[] stripByteCounts; | |
43 int t4Options; | |
44 int colorMapOffset; | |
45 | |
46 /* Encoder fields */ | |
47 ImageData image; | |
48 LEDataOutputStream ostr; | |
49 | |
50 static const int NO_VALUE = -1; | |
51 | |
52 static const short TAG_ImageWidth = 256; | |
53 static const short TAG_ImageLength = 257; | |
54 static const short TAG_BitsPerSample = 258; | |
55 static const short TAG_Compression = 259; | |
56 static const short TAG_PhotometricInterpretation = 262; | |
57 static const short TAG_StripOffsets = 273; | |
58 static const short TAG_SamplesPerPixel = 277; | |
59 static const short TAG_RowsPerStrip = 278; | |
60 static const short TAG_StripByteCounts = 279; | |
61 static const short TAG_XResolution = 282; | |
62 static const short TAG_YResolution = 283; | |
63 static const short TAG_T4Options = 292; | |
64 static const short TAG_ResolutionUnit = 296; | |
65 static const short TAG_ColorMap = 320; | |
66 | |
67 static const int TYPE_BYTE = 1; | |
68 static const int TYPE_ASCII = 2; | |
69 static const int TYPE_SHORT = 3; | |
70 static const int TYPE_LONG = 4; | |
71 static const int TYPE_RATIONAL = 5; | |
72 | |
73 /* Different compression schemes */ | |
74 static const int COMPRESSION_NONE = 1; | |
75 static const int COMPRESSION_CCITT_3_1 = 2; | |
76 static const int COMPRESSION_PACKBITS = 32773; | |
77 | |
78 static const int IFD_ENTRY_SIZE = 12; | |
79 | |
80 public this(TIFFRandomFileAccess file, bool isLittleEndian, ImageLoader loader) { | |
81 this.file = file; | |
82 this.isLittleEndian = isLittleEndian; | |
83 this.loader = loader; | |
84 } | |
85 | |
86 public this(ImageData image) { | |
87 this.image = image; | |
88 } | |
89 | |
90 /* PackBits decoder */ | |
91 int decodePackBits(byte[] src, byte[] dest, int offsetDest) { | |
92 int destIndex = offsetDest; | |
93 int srcIndex = 0; | |
94 while (srcIndex < src.length) { | |
95 byte n = src[srcIndex]; | |
96 if (0 <= n && n <= 127) { | |
97 /* Copy next n+1 bytes literally */ | |
98 System.arraycopy(src, ++srcIndex, dest, destIndex, n + 1); | |
99 srcIndex += n + 1; | |
100 destIndex += n + 1; | |
101 } else if (-127 <= n && n <= -1) { | |
102 /* Copy next byte -n+1 times */ | |
103 byte value = src[++srcIndex]; | |
104 for (int j = 0; j < -n + 1; j++) { | |
105 dest[destIndex++] = value; | |
106 } | |
107 srcIndex++; | |
108 } else { | |
109 /* Noop when n is -128 */ | |
110 srcIndex++; | |
111 } | |
112 } | |
113 /* Number of bytes copied */ | |
114 return destIndex - offsetDest; | |
115 } | |
116 | |
117 int getEntryValue(int type, byte[] buffer, int index) { | |
118 return toInt(buffer, index + 8, type); | |
119 } | |
120 | |
121 void getEntryValue(int type, byte[] buffer, int index, int[] values) { | |
122 int start = index + 8; | |
123 int size; | |
124 int offset = toInt(buffer, start, TYPE_LONG); | |
125 switch (type) { | |
126 case TYPE_SHORT: size = 2; break; | |
127 case TYPE_LONG: size = 4; break; | |
128 case TYPE_RATIONAL: size = 8; break; | |
129 case TYPE_ASCII: | |
130 case TYPE_BYTE: size = 1; break; | |
131 default: SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); return; | |
132 } | |
133 if (values.length * size > 4) { | |
134 buffer = new byte[values.length * size]; | |
135 file.seek(offset); | |
136 file.read(buffer); | |
137 start = 0; | |
138 } | |
139 for (int i = 0; i < values.length; i++) { | |
140 values[i] = toInt(buffer, start + i * size, type); | |
141 } | |
142 } | |
143 | |
144 void decodePixels(ImageData image) { | |
145 /* Each row is byte aligned */ | |
146 byte[] imageData = new byte[(imageWidth * depth + 7) / 8 * imageLength]; | |
147 image.data = imageData; | |
148 int destIndex = 0; | |
149 int length = stripOffsets.length; | |
150 for (int i = 0; i < length; i++) { | |
151 /* Read a strip */ | |
152 byte[] data = new byte[](stripByteCounts[i]); | |
153 file.seek(stripOffsets[i]); | |
154 file.read(data); | |
155 if (compression is COMPRESSION_NONE) { | |
156 System.arraycopy(data, 0, imageData, destIndex, data.length); | |
157 destIndex += data.length; | |
158 } else if (compression is COMPRESSION_PACKBITS) { | |
159 destIndex += decodePackBits(data, imageData, destIndex); | |
160 } else if (compression is COMPRESSION_CCITT_3_1 || compression is 3) { | |
161 TIFFModifiedHuffmanCodec codec = new TIFFModifiedHuffmanCodec(); | |
162 int nRows = rowsPerStrip; | |
163 if (i is length -1) { | |
164 int n = imageLength % rowsPerStrip; | |
165 if (n !is 0) nRows = n; | |
166 } | |
167 destIndex += codec.decode(data, imageData, destIndex, imageWidth, nRows); | |
168 } | |
169 if (loader.hasListeners()) { | |
170 loader.notifyListeners(new ImageLoaderEvent(loader, image, i, i is length - 1)); | |
171 } | |
172 } | |
173 } | |
174 | |
175 PaletteData getColorMap() { | |
176 int numColors = 1 << bitsPerSample[0]; | |
177 /* R, G, B entries are 16 bit wide (2 bytes) */ | |
178 int numBytes = 3 * 2 * numColors; | |
179 byte[] buffer = new byte[numBytes]; | |
180 file.seek(colorMapOffset); | |
181 file.read(buffer); | |
182 RGB[] colors = new RGB[numColors]; | |
183 /** | |
184 * SWT does not support 16-bit depth color formats. | |
185 * Convert the 16-bit data to 8-bit data. | |
186 * The correct way to do this is to multiply each | |
187 * 16 bit value by the value: | |
188 * (2^8 - 1) / (2^16 - 1). | |
189 * The fast way to do this is just to drop the low | |
190 * byte of the 16-bit value. | |
191 */ | |
192 int offset = isLittleEndian ? 1 : 0; | |
193 int startG = 2 * numColors; | |
194 int startB = startG + 2 * numColors; | |
195 for (int i = 0; i < numColors; i++) { | |
196 int r = buffer[offset] & 0xFF; | |
197 int g = buffer[startG + offset] & 0xFF; | |
198 int b = buffer[startB + offset] & 0xFF; | |
199 colors[i] = new RGB(r, g, b); | |
200 offset += 2; | |
201 } | |
202 return new PaletteData(colors); | |
203 } | |
204 | |
205 PaletteData getGrayPalette() { | |
206 int numColors = 1 << bitsPerSample[0]; | |
207 RGB[] rgbs = new RGB[numColors]; | |
208 for (int i = 0; i < numColors; i++) { | |
209 int value = i * 0xFF / (numColors - 1); | |
210 if (photometricInterpretation is 0) value = 0xFF - value; | |
211 rgbs[i] = new RGB(value, value, value); | |
212 } | |
213 return new PaletteData(rgbs); | |
214 } | |
215 | |
216 PaletteData getRGBPalette(int bitsR, int bitsG, int bitsB) { | |
217 int blueMask = 0; | |
218 for (int i = 0; i < bitsB; i++) { | |
219 blueMask |= 1 << i; | |
220 } | |
221 int greenMask = 0; | |
222 for (int i = bitsB; i < bitsB + bitsG; i++) { | |
223 greenMask |= 1 << i; | |
224 } | |
225 int redMask = 0; | |
226 for (int i = bitsB + bitsG; i < bitsB + bitsG + bitsR; i++) { | |
227 redMask |= 1 << i; | |
228 } | |
229 return new PaletteData(redMask, greenMask, blueMask); | |
230 } | |
231 | |
232 int formatStrips(int rowByteSize, int nbrRows, byte[] data, int maxStripByteSize, int offsetPostIFD, int extraBytes, int[][] strips) { | |
233 /* | |
234 * Calculate the nbr of required strips given the following requirements: | |
235 * - each strip should, if possible, not be greater than maxStripByteSize | |
236 * - each strip should contain 1 or more entire rows | |
237 * | |
238 * Format the strip fields arrays so that the image data is stored in one | |
239 * contiguous block. This block is stored after the IFD and after any tag | |
240 * info described in the IFD. | |
241 */ | |
242 int n, nbrRowsPerStrip; | |
243 if (rowByteSize > maxStripByteSize) { | |
244 /* Each strip contains 1 row */ | |
245 n = data.length / rowByteSize; | |
246 nbrRowsPerStrip = 1; | |
247 } else { | |
248 int nbr = (data.length + maxStripByteSize - 1) / maxStripByteSize; | |
249 nbrRowsPerStrip = nbrRows / nbr; | |
250 n = (nbrRows + nbrRowsPerStrip - 1) / nbrRowsPerStrip; | |
251 } | |
252 int stripByteSize = rowByteSize * nbrRowsPerStrip; | |
253 | |
254 int[] offsets = new int[n]; | |
255 int[] counts = new int[n]; | |
256 /* | |
257 * Nbr of bytes between the end of the IFD directory and the start of | |
258 * the image data. Keep space for at least the offsets and counts | |
259 * data, each field being TYPE_LONG (4 bytes). If other tags require | |
260 * space between the IFD and the image block, use the extraBytes | |
261 * parameter. | |
262 * If there is only one strip, the offsets and counts data is stored | |
263 * directly in the IFD and we need not reserve space for it. | |
264 */ | |
265 int postIFDData = n is 1 ? 0 : n * 2 * 4; | |
266 int startOffset = offsetPostIFD + extraBytes + postIFDData; /* offset of image data */ | |
267 | |
268 int offset = startOffset; | |
269 for (int i = 0; i < n; i++) { | |
270 /* | |
271 * Store all strips sequentially to allow us | |
272 * to copy all pixels in one contiguous area. | |
273 */ | |
274 offsets[i] = offset; | |
275 counts[i] = stripByteSize; | |
276 offset += stripByteSize; | |
277 } | |
278 /* The last strip may contain fewer rows */ | |
279 int mod = data.length % stripByteSize; | |
280 if (mod !is 0) counts[counts.length - 1] = mod; | |
281 | |
282 strips[0] = offsets; | |
283 strips[1] = counts; | |
284 return nbrRowsPerStrip; | |
285 } | |
286 | |
287 int[] formatColorMap(RGB[] rgbs) { | |
288 /* | |
289 * In a TIFF ColorMap, all red come first, followed by | |
290 * green and blue. All values must be converted from | |
291 * 8 bit to 16 bit. | |
292 */ | |
293 int[] colorMap = new int[rgbs.length * 3]; | |
294 int offsetGreen = rgbs.length; | |
295 int offsetBlue = rgbs.length * 2; | |
296 for (int i = 0; i < rgbs.length; i++) { | |
297 colorMap[i] = rgbs[i].red << 8 | rgbs[i].red; | |
298 colorMap[i + offsetGreen] = rgbs[i].green << 8 | rgbs[i].green; | |
299 colorMap[i + offsetBlue] = rgbs[i].blue << 8 | rgbs[i].blue; | |
300 } | |
301 return colorMap; | |
302 } | |
303 | |
304 void parseEntries(byte[] buffer) { | |
305 for (int offset = 0; offset < buffer.length; offset += IFD_ENTRY_SIZE) { | |
306 int tag = toInt(buffer, offset, TYPE_SHORT); | |
307 int type = toInt(buffer, offset + 2, TYPE_SHORT); | |
308 int count = toInt(buffer, offset + 4, TYPE_LONG); | |
309 switch (tag) { | |
310 case TAG_ImageWidth: { | |
311 imageWidth = getEntryValue(type, buffer, offset); | |
312 break; | |
313 } | |
314 case TAG_ImageLength: { | |
315 imageLength = getEntryValue(type, buffer, offset); | |
316 break; | |
317 } | |
318 case TAG_BitsPerSample: { | |
319 if (type !is TYPE_SHORT) SWT.error(SWT.ERROR_INVALID_IMAGE); | |
320 bitsPerSample = new int[count]; | |
321 getEntryValue(type, buffer, offset, bitsPerSample); | |
322 break; | |
323 } | |
324 case TAG_Compression: { | |
325 compression = getEntryValue(type, buffer, offset); | |
326 break; | |
327 } | |
328 case TAG_PhotometricInterpretation: { | |
329 photometricInterpretation = getEntryValue(type, buffer, offset); | |
330 break; | |
331 } | |
332 case TAG_StripOffsets: { | |
333 if (type !is TYPE_LONG && type !is TYPE_SHORT) SWT.error(SWT.ERROR_INVALID_IMAGE); | |
334 stripOffsets = new int[count]; | |
335 getEntryValue(type, buffer, offset, stripOffsets); | |
336 break; | |
337 } | |
338 case TAG_SamplesPerPixel: { | |
339 if (type !is TYPE_SHORT) SWT.error(SWT.ERROR_INVALID_IMAGE); | |
340 samplesPerPixel = getEntryValue(type, buffer, offset); | |
341 /* Only the basic 1 and 3 values are supported */ | |
342 if (samplesPerPixel !is 1 && samplesPerPixel !is 3) SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); | |
343 break; | |
344 } | |
345 case TAG_RowsPerStrip: { | |
346 rowsPerStrip = getEntryValue(type, buffer, offset); | |
347 break; | |
348 } | |
349 case TAG_StripByteCounts: { | |
350 stripByteCounts = new int[count]; | |
351 getEntryValue(type, buffer, offset, stripByteCounts); | |
352 break; | |
353 } | |
354 case TAG_XResolution: { | |
355 /* Ignored */ | |
356 break; | |
357 } | |
358 case TAG_YResolution: { | |
359 /* Ignored */ | |
360 break; | |
361 } | |
362 case TAG_T4Options: { | |
363 if (type !is TYPE_LONG) SWT.error(SWT.ERROR_INVALID_IMAGE); | |
364 t4Options = getEntryValue(type, buffer, offset); | |
365 if ((t4Options & 0x1) is 1) { | |
366 /* 2-dimensional coding is not supported */ | |
367 SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); | |
368 } | |
369 break; | |
370 } | |
371 case TAG_ResolutionUnit: { | |
372 /* Ignored */ | |
373 break; | |
374 } | |
375 case TAG_ColorMap: { | |
376 if (type !is TYPE_SHORT) SWT.error(SWT.ERROR_INVALID_IMAGE); | |
377 /* Get the offset of the colorMap (use TYPE_LONG) */ | |
378 colorMapOffset = getEntryValue(TYPE_LONG, buffer, offset); | |
379 break; | |
380 } | |
381 default: | |
382 } | |
383 } | |
384 } | |
385 | |
386 public ImageData read() { | |
387 /* Set TIFF default values */ | |
388 bitsPerSample = [1]; | |
389 colorMapOffset = NO_VALUE; | |
390 compression = 1; | |
391 imageLength = NO_VALUE; | |
392 imageWidth = NO_VALUE; | |
393 photometricInterpretation = NO_VALUE; | |
394 rowsPerStrip = Integer.MAX_VALUE; | |
395 samplesPerPixel = 1; | |
396 stripByteCounts = null; | |
397 stripOffsets = null; | |
398 | |
399 byte[] buffer = new byte[2]; | |
400 file.read(buffer); | |
401 int numberEntries = toInt(buffer, 0, TYPE_SHORT); | |
402 buffer = new byte[IFD_ENTRY_SIZE * numberEntries]; | |
403 file.read(buffer); | |
404 parseEntries(buffer); | |
405 | |
406 PaletteData palette = null; | |
407 depth = 0; | |
408 switch (photometricInterpretation) { | |
409 case 0: | |
410 case 1: { | |
411 /* Bilevel or Grayscale image */ | |
412 palette = getGrayPalette(); | |
413 depth = bitsPerSample[0]; | |
414 break; | |
415 } | |
416 case 2: { | |
417 /* RGB image */ | |
418 if (colorMapOffset !is NO_VALUE) SWT.error(SWT.ERROR_INVALID_IMAGE); | |
419 /* SamplesPerPixel 3 is the only value supported */ | |
420 palette = getRGBPalette(bitsPerSample[0], bitsPerSample[1], bitsPerSample[2]); | |
421 depth = bitsPerSample[0] + bitsPerSample[1] + bitsPerSample[2]; | |
422 break; | |
423 } | |
424 case 3: { | |
425 /* Palette Color image */ | |
426 if (colorMapOffset is NO_VALUE) SWT.error(SWT.ERROR_INVALID_IMAGE); | |
427 palette = getColorMap(); | |
428 depth = bitsPerSample[0]; | |
429 break; | |
430 } | |
431 default: { | |
432 SWT.error(SWT.ERROR_INVALID_IMAGE); | |
433 } | |
434 } | |
435 | |
436 ImageData image = ImageData.internal_new( | |
437 imageWidth, | |
438 imageLength, | |
439 depth, | |
440 palette, | |
441 1, | |
442 null, | |
443 0, | |
444 null, | |
445 null, | |
446 -1, | |
447 -1, | |
448 SWT.IMAGE_TIFF, | |
449 0, | |
450 0, | |
451 0, | |
452 0); | |
453 decodePixels(image); | |
454 return image; | |
455 } | |
456 | |
457 int toInt(byte[] buffer, int i, int type) { | |
458 if (type is TYPE_LONG) { | |
459 return isLittleEndian ? | |
460 (buffer[i] & 0xFF) | ((buffer[i + 1] & 0xFF) << 8) | ((buffer[i + 2] & 0xFF) << 16) | ((buffer[i + 3] & 0xFF) << 24) : | |
461 (buffer[i + 3] & 0xFF) | ((buffer[i + 2] & 0xFF) << 8) | ((buffer[i + 1] & 0xFF) << 16) | ((buffer[i] & 0xFF) << 24); | |
462 } | |
463 if (type is TYPE_SHORT) { | |
464 return isLittleEndian ? | |
465 (buffer[i] & 0xFF) | ((buffer[i + 1] & 0xFF) << 8) : | |
466 (buffer[i + 1] & 0xFF) | ((buffer[i] & 0xFF) << 8); | |
467 } | |
468 /* Invalid type */ | |
469 SWT.error(SWT.ERROR_INVALID_IMAGE); | |
470 return -1; | |
471 } | |
472 | |
473 void write(int photometricInterpretation) { | |
474 bool isRGB = photometricInterpretation is 2; | |
475 bool isColorMap = photometricInterpretation is 3; | |
476 bool isBiLevel = photometricInterpretation is 0 || photometricInterpretation is 1; | |
477 | |
478 int imageWidth = image.width; | |
479 int imageLength = image.height; | |
480 int rowByteSize = image.bytesPerLine; | |
481 | |
482 int numberEntries = isBiLevel ? 9 : 11; | |
483 int lengthDirectory = 2 + 12 * numberEntries + 4; | |
484 /* Offset following the header and the directory */ | |
485 int nextOffset = 8 + lengthDirectory; | |
486 | |
487 /* Extra space used by XResolution and YResolution values */ | |
488 int extraBytes = 16; | |
489 | |
490 int[] colorMap = null; | |
491 if (isColorMap) { | |
492 PaletteData palette = image.palette; | |
493 RGB[] rgbs = palette.getRGBs(); | |
494 colorMap = formatColorMap(rgbs); | |
495 /* The number of entries of the Color Map must match the bitsPerSample field */ | |
496 if (colorMap.length !is 3 * 1 << image.depth) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); | |
497 /* Extra space used by ColorMap values */ | |
498 extraBytes += colorMap.length * 2; | |
499 } | |
500 if (isRGB) { | |
501 /* Extra space used by BitsPerSample values */ | |
502 extraBytes += 6; | |
503 } | |
504 /* TIFF recommends storing the data in strips of no more than 8 Ko */ | |
505 byte[] data = image.data; | |
506 int[][] strips = new int[][](2); | |
507 int nbrRowsPerStrip = formatStrips(rowByteSize, imageLength, data, 8192, nextOffset, extraBytes, strips); | |
508 int[] stripOffsets = strips[0]; | |
509 int[] stripByteCounts = strips[1]; | |
510 | |
511 int bitsPerSampleOffset = NO_VALUE; | |
512 if (isRGB) { | |
513 bitsPerSampleOffset = nextOffset; | |
514 nextOffset += 6; | |
515 } | |
516 int stripOffsetsOffset = NO_VALUE, stripByteCountsOffset = NO_VALUE; | |
517 int xResolutionOffset, yResolutionOffset, colorMapOffset = NO_VALUE; | |
518 int cnt = stripOffsets.length; | |
519 if (cnt > 1) { | |
520 stripOffsetsOffset = nextOffset; | |
521 nextOffset += 4 * cnt; | |
522 stripByteCountsOffset = nextOffset; | |
523 nextOffset += 4 * cnt; | |
524 } | |
525 xResolutionOffset = nextOffset; | |
526 nextOffset += 8; | |
527 yResolutionOffset = nextOffset; | |
528 nextOffset += 8; | |
529 if (isColorMap) { | |
530 colorMapOffset = nextOffset; | |
531 nextOffset += colorMap.length * 2; | |
532 } | |
533 /* TIFF header */ | |
534 writeHeader(); | |
535 | |
536 /* Image File Directory */ | |
537 ostr.writeShort(numberEntries); | |
538 writeEntry(TAG_ImageWidth, TYPE_LONG, 1, imageWidth); | |
539 writeEntry(TAG_ImageLength, TYPE_LONG, 1, imageLength); | |
540 if (isColorMap) writeEntry(TAG_BitsPerSample, TYPE_SHORT, 1, image.depth); | |
541 if (isRGB) writeEntry(TAG_BitsPerSample, TYPE_SHORT, 3, bitsPerSampleOffset); | |
542 writeEntry(TAG_Compression, TYPE_SHORT, 1, COMPRESSION_NONE); | |
543 writeEntry(TAG_PhotometricInterpretation, TYPE_SHORT, 1, photometricInterpretation); | |
544 writeEntry(TAG_StripOffsets, TYPE_LONG, cnt, cnt > 1 ? stripOffsetsOffset : stripOffsets[0]); | |
545 if (isRGB) writeEntry(TAG_SamplesPerPixel, TYPE_SHORT, 1, 3); | |
546 writeEntry(TAG_RowsPerStrip, TYPE_LONG, 1, nbrRowsPerStrip); | |
547 writeEntry(TAG_StripByteCounts, TYPE_LONG, cnt, cnt > 1 ? stripByteCountsOffset : stripByteCounts[0]); | |
548 writeEntry(TAG_XResolution, TYPE_RATIONAL, 1, xResolutionOffset); | |
549 writeEntry(TAG_YResolution, TYPE_RATIONAL, 1, yResolutionOffset); | |
550 if (isColorMap) writeEntry(TAG_ColorMap, TYPE_SHORT, colorMap.length, colorMapOffset); | |
551 /* Offset of next IFD (0 for last IFD) */ | |
552 ostr.writeInt(0); | |
553 | |
554 /* Values longer than 4 bytes Section */ | |
555 | |
556 /* BitsPerSample 8,8,8 */ | |
557 if (isRGB) for (int i = 0; i < 3; i++) ostr.writeShort(8); | |
558 if (cnt > 1) { | |
559 for (int i = 0; i < cnt; i++) ostr.writeInt(stripOffsets[i]); | |
560 for (int i = 0; i < cnt; i++) ostr.writeInt(stripByteCounts[i]); | |
561 } | |
562 /* XResolution and YResolution set to 300 dpi */ | |
563 for (int i = 0; i < 2; i++) { | |
564 ostr.writeInt(300); | |
565 ostr.writeInt(1); | |
566 } | |
567 /* ColorMap */ | |
568 if (isColorMap) for (int i = 0; i < colorMap.length; i++) ostr.writeShort(colorMap[i]); | |
569 | |
570 /* Image Data */ | |
571 ostr.write(data); | |
572 } | |
573 | |
574 void writeEntry(short tag, int type, int count, int value) { | |
575 ostr.writeShort(tag); | |
576 ostr.writeShort(type); | |
577 ostr.writeInt(count); | |
578 ostr.writeInt(value); | |
579 } | |
580 | |
581 void writeHeader() { | |
582 /* little endian */ | |
583 ostr.write(0x49); | |
584 ostr.write(0x49); | |
585 | |
586 /* TIFF identifier */ | |
587 ostr.writeShort(42); | |
588 /* | |
589 * Offset of the first IFD is chosen to be 8. | |
590 * It is word aligned and immediately after this header. | |
591 */ | |
592 ostr.writeInt(8); | |
593 } | |
594 | |
595 void writeToStream(LEDataOutputStream byteStream) { | |
596 ostr = byteStream; | |
597 int photometricInterpretation = -1; | |
598 | |
599 /* Scanline pad must be 1 */ | |
600 if (image.scanlinePad !is 1) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); | |
601 switch (image.depth) { | |
602 case 1: { | |
603 /* Palette must be black and white or white and black */ | |
604 PaletteData palette = image.palette; | |
605 RGB[] rgbs = palette.colors; | |
606 if (palette.isDirect || rgbs is null || rgbs.length !is 2) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); | |
607 RGB rgb0 = rgbs[0]; | |
608 RGB rgb1 = rgbs[1]; | |
609 if (!(rgb0.red is rgb0.green && rgb0.green is rgb0.blue && | |
610 rgb1.red is rgb1.green && rgb1.green is rgb1.blue && | |
611 ((rgb0.red is 0x0 && rgb1.red is 0xFF) || (rgb0.red is 0xFF && rgb1.red is 0x0)))) { | |
612 SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); | |
613 } | |
614 /* 0 means a color index of 0 is imaged as white */ | |
615 photometricInterpretation = image.palette.colors[0].red is 0xFF ? 0 : 1; | |
616 break; | |
617 } | |
618 case 4: | |
619 case 8: { | |
620 photometricInterpretation = 3; | |
621 break; | |
622 } | |
623 case 24: { | |
624 photometricInterpretation = 2; | |
625 break; | |
626 } | |
627 default: { | |
628 SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); | |
629 } | |
630 } | |
631 write(photometricInterpretation); | |
632 } | |
633 | |
634 } |