Mercurial > projects > dwt-mac
comparison dwt/internal/image/TIFFDirectory.d @ 34:5123b17c98ef
Ported dwt.events.*, dwt.graphics.GC, Region, dwt.internal.image.*
author | Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com> |
---|---|
date | Sun, 14 Sep 2008 01:45:57 +0200 |
parents | b903c16b6f48 |
children |
comparison
equal
deleted
inserted
replaced
33:965ac0a77267 | 34:5123b17c98ef |
---|---|
5 * which accompanies this distribution, and is available at | 5 * which accompanies this distribution, and is available at |
6 * http://www.eclipse.org/legal/epl-v10.html | 6 * http://www.eclipse.org/legal/epl-v10.html |
7 * | 7 * |
8 * Contributors: | 8 * Contributors: |
9 * IBM Corporation - initial API and implementation | 9 * IBM Corporation - initial API and implementation |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
10 *******************************************************************************/ | 12 *******************************************************************************/ |
11 module dwt.internal.image; | 13 module dwt.internal.image.TIFFDirectory; |
12 | 14 |
13 | 15 import dwt.internal.image.TIFFRandomFileAccess; |
14 import java.io.IOException; | 16 import dwt.internal.image.TIFFModifiedHuffmanCodec; |
15 | 17 import dwt.internal.image.LEDataOutputStream; |
16 import dwt.DWT; | |
17 import dwt.graphics.ImageData; | 18 import dwt.graphics.ImageData; |
19 import dwt.graphics.ImageLoaderEvent; | |
18 import dwt.graphics.ImageLoader; | 20 import dwt.graphics.ImageLoader; |
19 import dwt.graphics.ImageLoaderEvent; | |
20 import dwt.graphics.PaletteData; | 21 import dwt.graphics.PaletteData; |
21 import dwt.graphics.RGB; | 22 import dwt.graphics.RGB; |
23 import dwt.DWT; | |
24 import dwt.dwthelper.utils; | |
22 | 25 |
23 final class TIFFDirectory { | 26 final class TIFFDirectory { |
24 | 27 |
25 TIFFRandomFileAccess file; | 28 TIFFRandomFileAccess file; |
26 bool isLittleEndian; | 29 bool isLittleEndian; |
27 ImageLoader loader; | 30 ImageLoader loader; |
28 int depth; | 31 int depth; |
29 | 32 |
30 /* Directory fields */ | 33 /* Directory fields */ |
31 int imageWidth; | 34 int imageWidth; |
32 int imageLength; | 35 int imageLength; |
33 int[] bitsPerSample; | 36 int[] bitsPerSample; |
34 int compression; | 37 int compression; |
37 int samplesPerPixel; | 40 int samplesPerPixel; |
38 int rowsPerStrip; | 41 int rowsPerStrip; |
39 int[] stripByteCounts; | 42 int[] stripByteCounts; |
40 int t4Options; | 43 int t4Options; |
41 int colorMapOffset; | 44 int colorMapOffset; |
42 | 45 |
43 /* Encoder fields */ | 46 /* Encoder fields */ |
44 ImageData image; | 47 ImageData image; |
45 LEDataOutputStream out; | 48 LEDataOutputStream ostr; |
46 | 49 |
47 static final int NO_VALUE = -1; | 50 static const int NO_VALUE = -1; |
48 | 51 |
49 static final short TAG_ImageWidth = 256; | 52 static const short TAG_ImageWidth = 256; |
50 static final short TAG_ImageLength = 257; | 53 static const short TAG_ImageLength = 257; |
51 static final short TAG_BitsPerSample = 258; | 54 static const short TAG_BitsPerSample = 258; |
52 static final short TAG_Compression = 259; | 55 static const short TAG_Compression = 259; |
53 static final short TAG_PhotometricInterpretation = 262; | 56 static const short TAG_PhotometricInterpretation = 262; |
54 static final short TAG_StripOffsets = 273; | 57 static const short TAG_StripOffsets = 273; |
55 static final short TAG_SamplesPerPixel = 277; | 58 static const short TAG_SamplesPerPixel = 277; |
56 static final short TAG_RowsPerStrip = 278; | 59 static const short TAG_RowsPerStrip = 278; |
57 static final short TAG_StripByteCounts = 279; | 60 static const short TAG_StripByteCounts = 279; |
58 static final short TAG_XResolution = 282; | 61 static const short TAG_XResolution = 282; |
59 static final short TAG_YResolution = 283; | 62 static const short TAG_YResolution = 283; |
60 static final short TAG_T4Options = 292; | 63 static const short TAG_T4Options = 292; |
61 static final short TAG_ResolutionUnit = 296; | 64 static const short TAG_ResolutionUnit = 296; |
62 static final short TAG_ColorMap = 320; | 65 static const short TAG_ColorMap = 320; |
63 | 66 |
64 static final int TYPE_BYTE = 1; | 67 static const int TYPE_BYTE = 1; |
65 static final int TYPE_ASCII = 2; | 68 static const int TYPE_ASCII = 2; |
66 static final int TYPE_SHORT = 3; | 69 static const int TYPE_SHORT = 3; |
67 static final int TYPE_LONG = 4; | 70 static const int TYPE_LONG = 4; |
68 static final int TYPE_RATIONAL = 5; | 71 static const int TYPE_RATIONAL = 5; |
69 | 72 |
70 /* Different compression schemes */ | 73 /* Different compression schemes */ |
71 static final int COMPRESSION_NONE = 1; | 74 static const int COMPRESSION_NONE = 1; |
72 static final int COMPRESSION_CCITT_3_1 = 2; | 75 static const int COMPRESSION_CCITT_3_1 = 2; |
73 static final int COMPRESSION_PACKBITS = 32773; | 76 static const int COMPRESSION_PACKBITS = 32773; |
74 | 77 |
75 static final int IFD_ENTRY_SIZE = 12; | 78 static const int IFD_ENTRY_SIZE = 12; |
76 | 79 |
77 public this(TIFFRandomFileAccess file, bool isLittleEndian, ImageLoader loader) { | 80 public this(TIFFRandomFileAccess file, bool isLittleEndian, ImageLoader loader) { |
78 this.file = file; | 81 this.file = file; |
79 this.isLittleEndian = isLittleEndian; | 82 this.isLittleEndian = isLittleEndian; |
80 this.loader = loader; | 83 this.loader = loader; |
81 } | 84 } |
91 while (srcIndex < src.length) { | 94 while (srcIndex < src.length) { |
92 byte n = src[srcIndex]; | 95 byte n = src[srcIndex]; |
93 if (0 <= n && n <= 127) { | 96 if (0 <= n && n <= 127) { |
94 /* Copy next n+1 bytes literally */ | 97 /* Copy next n+1 bytes literally */ |
95 System.arraycopy(src, ++srcIndex, dest, destIndex, n + 1); | 98 System.arraycopy(src, ++srcIndex, dest, destIndex, n + 1); |
96 srcIndex += n + 1; | 99 srcIndex += n + 1; |
97 destIndex += n + 1; | 100 destIndex += n + 1; |
98 } else if (-127 <= n && n <= -1) { | 101 } else if (-127 <= n && n <= -1) { |
99 /* Copy next byte -n+1 times */ | 102 /* Copy next byte -n+1 times */ |
100 byte value = src[++srcIndex]; | 103 byte value = src[++srcIndex]; |
101 for (int j = 0; j < -n + 1; j++) { | 104 for (int j = 0; j < -n + 1; j++) { |
102 dest[destIndex++] = value; | 105 dest[destIndex++] = value; |
113 | 116 |
114 int getEntryValue(int type, byte[] buffer, int index) { | 117 int getEntryValue(int type, byte[] buffer, int index) { |
115 return toInt(buffer, index + 8, type); | 118 return toInt(buffer, index + 8, type); |
116 } | 119 } |
117 | 120 |
118 void getEntryValue(int type, byte[] buffer, int index, int[] values) { | 121 void getEntryValue(int type, byte[] buffer, int index, int[] values) { |
119 int start = index + 8; | 122 int start = index + 8; |
120 int size; | 123 int size; |
121 int offset = toInt(buffer, start, TYPE_LONG); | 124 int offset = toInt(buffer, start, TYPE_LONG); |
122 switch (type) { | 125 switch (type) { |
123 case TYPE_SHORT: size = 2; break; | 126 case TYPE_SHORT: size = 2; break; |
124 case TYPE_LONG: size = 4; break; | 127 case TYPE_LONG: size = 4; break; |
125 case TYPE_RATIONAL: size = 8; break; | 128 case TYPE_RATIONAL: size = 8; break; |
126 case TYPE_ASCII: | 129 case TYPE_ASCII: |
127 case TYPE_BYTE: size = 1; break; | 130 case TYPE_BYTE: size = 1; break; |
128 default: DWT.error(DWT.ERROR_UNSUPPORTED_FORMAT); return; | 131 default: DWT.error(DWT.ERROR_UNSUPPORTED_FORMAT); return; |
129 } | 132 } |
130 if (values.length * size > 4) { | 133 if (values.length * size > 4) { |
131 buffer = new byte[values.length * size]; | 134 buffer = new byte[values.length * size]; |
132 file.seek(offset); | 135 file.seek(offset); |
133 file.read(buffer); | 136 file.read(buffer); |
134 start = 0; | 137 start = 0; |
135 } | 138 } |
136 for (int i = 0; i < values.length; i++) { | 139 for (int i = 0; i < values.length; i++) { |
137 values[i] = toInt(buffer, start + i * size, type); | 140 values[i] = toInt(buffer, start + i * size, type); |
138 } | 141 } |
139 } | 142 } |
140 | 143 |
141 void decodePixels(ImageData image) { | 144 void decodePixels(ImageData image) { |
142 /* Each row is byte aligned */ | 145 /* Each row is byte aligned */ |
143 byte[] imageData = new byte[(imageWidth * depth + 7) / 8 * imageLength]; | 146 byte[] imageData = new byte[(imageWidth * depth + 7) / 8 * imageLength]; |
144 image.data = imageData; | 147 image.data = imageData; |
145 int destIndex = 0; | 148 int destIndex = 0; |
146 int length = stripOffsets.length; | 149 int length = stripOffsets.length; |
147 for (int i = 0; i < length; i++) { | 150 for (int i = 0; i < length; i++) { |
148 /* Read a strip */ | 151 /* Read a strip */ |
149 byte[] data = new byte[stripByteCounts[i]]; | 152 byte[] data = new byte[](stripByteCounts[i]); |
150 file.seek(stripOffsets[i]); | 153 file.seek(stripOffsets[i]); |
151 file.read(data); | 154 file.read(data); |
152 if (compression is COMPRESSION_NONE) { | 155 if (compression is COMPRESSION_NONE) { |
153 System.arraycopy(data, 0, imageData, destIndex, data.length); | 156 System.arraycopy(data, 0, imageData, destIndex, data.length); |
154 destIndex += data.length; | 157 destIndex += data.length; |
167 loader.notifyListeners(new ImageLoaderEvent(loader, image, i, i is length - 1)); | 170 loader.notifyListeners(new ImageLoaderEvent(loader, image, i, i is length - 1)); |
168 } | 171 } |
169 } | 172 } |
170 } | 173 } |
171 | 174 |
172 PaletteData getColorMap() { | 175 PaletteData getColorMap() { |
173 int numColors = 1 << bitsPerSample[0]; | 176 int numColors = 1 << bitsPerSample[0]; |
174 /* R, G, B entries are 16 bit wide (2 bytes) */ | 177 /* R, G, B entries are 16 bit wide (2 bytes) */ |
175 int numBytes = 3 * 2 * numColors; | 178 int numBytes = 3 * 2 * numColors; |
176 byte[] buffer = new byte[numBytes]; | 179 byte[] buffer = new byte[numBytes]; |
177 file.seek(colorMapOffset); | 180 file.seek(colorMapOffset); |
208 rgbs[i] = new RGB(value, value, value); | 211 rgbs[i] = new RGB(value, value, value); |
209 } | 212 } |
210 return new PaletteData(rgbs); | 213 return new PaletteData(rgbs); |
211 } | 214 } |
212 | 215 |
213 PaletteData getRGBPalette(int bitsR, int bitsG, int bitsB) { | 216 PaletteData getRGBPalette(int bitsR, int bitsG, int bitsB) { |
214 int blueMask = 0; | 217 int blueMask = 0; |
215 for (int i = 0; i < bitsB; i++) { | 218 for (int i = 0; i < bitsB; i++) { |
216 blueMask |= 1 << i; | 219 blueMask |= 1 << i; |
217 } | 220 } |
218 int greenMask = 0; | 221 int greenMask = 0; |
220 greenMask |= 1 << i; | 223 greenMask |= 1 << i; |
221 } | 224 } |
222 int redMask = 0; | 225 int redMask = 0; |
223 for (int i = bitsB + bitsG; i < bitsB + bitsG + bitsR; i++) { | 226 for (int i = bitsB + bitsG; i < bitsB + bitsG + bitsR; i++) { |
224 redMask |= 1 << i; | 227 redMask |= 1 << i; |
225 } | 228 } |
226 return new PaletteData(redMask, greenMask, blueMask); | 229 return new PaletteData(redMask, greenMask, blueMask); |
227 } | 230 } |
228 | 231 |
229 int formatStrips(int rowByteSize, int nbrRows, byte[] data, int maxStripByteSize, int offsetPostIFD, int extraBytes, int[][] strips) { | 232 int formatStrips(int rowByteSize, int nbrRows, byte[] data, int maxStripByteSize, int offsetPostIFD, int extraBytes, int[][] strips) { |
230 /* | 233 /* |
231 * Calculate the nbr of required strips given the following requirements: | 234 * Calculate the nbr of required strips given the following requirements: |
232 * - each strip should, if possible, not be greater than maxStripByteSize | 235 * - each strip should, if possible, not be greater than maxStripByteSize |
233 * - each strip should contain 1 or more entire rows | 236 * - each strip should contain 1 or more entire rows |
234 * | 237 * |
235 * Format the strip fields arrays so that the image data is stored in one | 238 * Format the strip fields arrays so that the image data is stored in one |
236 * contiguous block. This block is stored after the IFD and after any tag | 239 * contiguous block. This block is stored after the IFD and after any tag |
237 * info described in the IFD. | 240 * info described in the IFD. |
238 */ | 241 */ |
239 int n, nbrRowsPerStrip; | 242 int n, nbrRowsPerStrip; |
242 n = data.length / rowByteSize; | 245 n = data.length / rowByteSize; |
243 nbrRowsPerStrip = 1; | 246 nbrRowsPerStrip = 1; |
244 } else { | 247 } else { |
245 int nbr = (data.length + maxStripByteSize - 1) / maxStripByteSize; | 248 int nbr = (data.length + maxStripByteSize - 1) / maxStripByteSize; |
246 nbrRowsPerStrip = nbrRows / nbr; | 249 nbrRowsPerStrip = nbrRows / nbr; |
247 n = (nbrRows + nbrRowsPerStrip - 1) / nbrRowsPerStrip; | 250 n = (nbrRows + nbrRowsPerStrip - 1) / nbrRowsPerStrip; |
248 } | 251 } |
249 int stripByteSize = rowByteSize * nbrRowsPerStrip; | 252 int stripByteSize = rowByteSize * nbrRowsPerStrip; |
250 | 253 |
251 int[] offsets = new int[n]; | 254 int[] offsets = new int[n]; |
252 int[] counts = new int[n]; | 255 int[] counts = new int[n]; |
253 /* | 256 /* |
254 * Nbr of bytes between the end of the IFD directory and the start of | 257 * Nbr of bytes between the end of the IFD directory and the start of |
255 * the image data. Keep space for at least the offsets and counts | 258 * the image data. Keep space for at least the offsets and counts |
256 * data, each field being TYPE_LONG (4 bytes). If other tags require | 259 * data, each field being TYPE_LONG (4 bytes). If other tags require |
257 * space between the IFD and the image block, use the extraBytes | 260 * space between the IFD and the image block, use the extraBytes |
258 * parameter. | 261 * parameter. |
259 * If there is only one strip, the offsets and counts data is stored | 262 * If there is only one strip, the offsets and counts data is stored |
260 * directly in the IFD and we need not reserve space for it. | 263 * directly in the IFD and we need not reserve space for it. |
261 */ | 264 */ |
262 int postIFDData = n is 1 ? 0 : n * 2 * 4; | 265 int postIFDData = n is 1 ? 0 : n * 2 * 4; |
263 int startOffset = offsetPostIFD + extraBytes + postIFDData; /* offset of image data */ | 266 int startOffset = offsetPostIFD + extraBytes + postIFDData; /* offset of image data */ |
264 | 267 |
265 int offset = startOffset; | 268 int offset = startOffset; |
266 for (int i = 0; i < n; i++) { | 269 for (int i = 0; i < n; i++) { |
267 /* | 270 /* |
268 * Store all strips sequentially to allow us | 271 * Store all strips sequentially to allow us |
269 * to copy all pixels in one contiguous area. | 272 * to copy all pixels in one contiguous area. |
270 */ | 273 */ |
271 offsets[i] = offset; | 274 offsets[i] = offset; |
272 counts[i] = stripByteSize; | 275 counts[i] = stripByteSize; |
273 offset += stripByteSize; | 276 offset += stripByteSize; |
274 } | 277 } |
275 /* The last strip may contain fewer rows */ | 278 /* The last strip may contain fewer rows */ |
276 int mod = data.length % stripByteSize; | 279 int mod = data.length % stripByteSize; |
277 if (mod !is 0) counts[counts.length - 1] = mod; | 280 if (mod !is 0) counts[counts.length - 1] = mod; |
278 | 281 |
279 strips[0] = offsets; | 282 strips[0] = offsets; |
280 strips[1] = counts; | 283 strips[1] = counts; |
281 return nbrRowsPerStrip; | 284 return nbrRowsPerStrip; |
282 } | 285 } |
283 | 286 |
284 int[] formatColorMap(RGB[] rgbs) { | 287 int[] formatColorMap(RGB[] rgbs) { |
285 /* | 288 /* |
286 * In a TIFF ColorMap, all red come first, followed by | 289 * In a TIFF ColorMap, all red come first, followed by |
287 * green and blue. All values must be converted from | 290 * green and blue. All values must be converted from |
288 * 8 bit to 16 bit. | 291 * 8 bit to 16 bit. |
289 */ | 292 */ |
290 int[] colorMap = new int[rgbs.length * 3]; | 293 int[] colorMap = new int[rgbs.length * 3]; |
291 int offsetGreen = rgbs.length; | 294 int offsetGreen = rgbs.length; |
292 int offsetBlue = rgbs.length * 2; | 295 int offsetBlue = rgbs.length * 2; |
293 for (int i = 0; i < rgbs.length; i++) { | 296 for (int i = 0; i < rgbs.length; i++) { |
296 colorMap[i + offsetBlue] = rgbs[i].blue << 8 | rgbs[i].blue; | 299 colorMap[i + offsetBlue] = rgbs[i].blue << 8 | rgbs[i].blue; |
297 } | 300 } |
298 return colorMap; | 301 return colorMap; |
299 } | 302 } |
300 | 303 |
301 void parseEntries(byte[] buffer) { | 304 void parseEntries(byte[] buffer) { |
302 for (int offset = 0; offset < buffer.length; offset += IFD_ENTRY_SIZE) { | 305 for (int offset = 0; offset < buffer.length; offset += IFD_ENTRY_SIZE) { |
303 int tag = toInt(buffer, offset, TYPE_SHORT); | 306 int tag = toInt(buffer, offset, TYPE_SHORT); |
304 int type = toInt(buffer, offset + 2, TYPE_SHORT); | 307 int type = toInt(buffer, offset + 2, TYPE_SHORT); |
305 int count = toInt(buffer, offset + 4, TYPE_LONG); | 308 int count = toInt(buffer, offset + 4, TYPE_LONG); |
306 switch (tag) { | 309 switch (tag) { |
373 if (type !is TYPE_SHORT) DWT.error(DWT.ERROR_INVALID_IMAGE); | 376 if (type !is TYPE_SHORT) DWT.error(DWT.ERROR_INVALID_IMAGE); |
374 /* Get the offset of the colorMap (use TYPE_LONG) */ | 377 /* Get the offset of the colorMap (use TYPE_LONG) */ |
375 colorMapOffset = getEntryValue(TYPE_LONG, buffer, offset); | 378 colorMapOffset = getEntryValue(TYPE_LONG, buffer, offset); |
376 break; | 379 break; |
377 } | 380 } |
378 } | 381 default: |
379 } | 382 } |
380 } | 383 } |
381 | 384 } |
382 public ImageData read() { | 385 |
386 public ImageData read() { | |
383 /* Set TIFF default values */ | 387 /* Set TIFF default values */ |
384 bitsPerSample = new int[] {1}; | 388 bitsPerSample = [1]; |
385 colorMapOffset = NO_VALUE; | 389 colorMapOffset = NO_VALUE; |
386 compression = 1; | 390 compression = 1; |
387 imageLength = NO_VALUE; | 391 imageLength = NO_VALUE; |
388 imageWidth = NO_VALUE; | 392 imageWidth = NO_VALUE; |
389 photometricInterpretation = NO_VALUE; | 393 photometricInterpretation = NO_VALUE; |
390 rowsPerStrip = Integer.MAX_VALUE; | 394 rowsPerStrip = Integer.MAX_VALUE; |
391 samplesPerPixel = 1; | 395 samplesPerPixel = 1; |
392 stripByteCounts = null; | 396 stripByteCounts = null; |
393 stripOffsets = null; | 397 stripOffsets = null; |
394 | 398 |
395 byte[] buffer = new byte[2]; | 399 byte[] buffer = new byte[2]; |
396 file.read(buffer); | 400 file.read(buffer); |
397 int numberEntries = toInt(buffer, 0, TYPE_SHORT); | 401 int numberEntries = toInt(buffer, 0, TYPE_SHORT); |
398 buffer = new byte[IFD_ENTRY_SIZE * numberEntries]; | 402 buffer = new byte[IFD_ENTRY_SIZE * numberEntries]; |
399 file.read(buffer); | 403 file.read(buffer); |
400 parseEntries(buffer); | 404 parseEntries(buffer); |
401 | 405 |
402 PaletteData palette = null; | 406 PaletteData palette = null; |
403 depth = 0; | 407 depth = 0; |
404 switch (photometricInterpretation) { | 408 switch (photometricInterpretation) { |
405 case 0: | 409 case 0: |
406 case 1: { | 410 case 1: { |
413 /* RGB image */ | 417 /* RGB image */ |
414 if (colorMapOffset !is NO_VALUE) DWT.error(DWT.ERROR_INVALID_IMAGE); | 418 if (colorMapOffset !is NO_VALUE) DWT.error(DWT.ERROR_INVALID_IMAGE); |
415 /* SamplesPerPixel 3 is the only value supported */ | 419 /* SamplesPerPixel 3 is the only value supported */ |
416 palette = getRGBPalette(bitsPerSample[0], bitsPerSample[1], bitsPerSample[2]); | 420 palette = getRGBPalette(bitsPerSample[0], bitsPerSample[1], bitsPerSample[2]); |
417 depth = bitsPerSample[0] + bitsPerSample[1] + bitsPerSample[2]; | 421 depth = bitsPerSample[0] + bitsPerSample[1] + bitsPerSample[2]; |
418 break; | 422 break; |
419 } | 423 } |
420 case 3: { | 424 case 3: { |
421 /* Palette Color image */ | 425 /* Palette Color image */ |
422 if (colorMapOffset is NO_VALUE) DWT.error(DWT.ERROR_INVALID_IMAGE); | 426 if (colorMapOffset is NO_VALUE) DWT.error(DWT.ERROR_INVALID_IMAGE); |
423 palette = getColorMap(); | 427 palette = getColorMap(); |
429 } | 433 } |
430 } | 434 } |
431 | 435 |
432 ImageData image = ImageData.internal_new( | 436 ImageData image = ImageData.internal_new( |
433 imageWidth, | 437 imageWidth, |
434 imageLength, | 438 imageLength, |
435 depth, | 439 depth, |
436 palette, | 440 palette, |
437 1, | 441 1, |
438 null, | 442 null, |
439 0, | 443 0, |
450 return image; | 454 return image; |
451 } | 455 } |
452 | 456 |
453 int toInt(byte[] buffer, int i, int type) { | 457 int toInt(byte[] buffer, int i, int type) { |
454 if (type is TYPE_LONG) { | 458 if (type is TYPE_LONG) { |
455 return isLittleEndian ? | 459 return isLittleEndian ? |
456 (buffer[i] & 0xFF) | ((buffer[i + 1] & 0xFF) << 8) | ((buffer[i + 2] & 0xFF) << 16) | ((buffer[i + 3] & 0xFF) << 24) : | 460 (buffer[i] & 0xFF) | ((buffer[i + 1] & 0xFF) << 8) | ((buffer[i + 2] & 0xFF) << 16) | ((buffer[i + 3] & 0xFF) << 24) : |
457 (buffer[i + 3] & 0xFF) | ((buffer[i + 2] & 0xFF) << 8) | ((buffer[i + 1] & 0xFF) << 16) | ((buffer[i] & 0xFF) << 24); | 461 (buffer[i + 3] & 0xFF) | ((buffer[i + 2] & 0xFF) << 8) | ((buffer[i + 1] & 0xFF) << 16) | ((buffer[i] & 0xFF) << 24); |
458 } | 462 } |
459 if (type is TYPE_SHORT) { | 463 if (type is TYPE_SHORT) { |
460 return isLittleEndian ? | 464 return isLittleEndian ? |
461 (buffer[i] & 0xFF) | ((buffer[i + 1] & 0xFF) << 8) : | 465 (buffer[i] & 0xFF) | ((buffer[i + 1] & 0xFF) << 8) : |
462 (buffer[i + 1] & 0xFF) | ((buffer[i] & 0xFF) << 8); | 466 (buffer[i + 1] & 0xFF) | ((buffer[i] & 0xFF) << 8); |
463 } | 467 } |
464 /* Invalid type */ | 468 /* Invalid type */ |
465 DWT.error(DWT.ERROR_INVALID_IMAGE); | 469 DWT.error(DWT.ERROR_INVALID_IMAGE); |
466 return -1; | 470 return -1; |
467 } | 471 } |
468 | 472 |
469 void write(int photometricInterpretation) { | 473 void write(int photometricInterpretation) { |
470 bool isRGB = photometricInterpretation is 2; | 474 bool isRGB = photometricInterpretation is 2; |
471 bool isColorMap = photometricInterpretation is 3; | 475 bool isColorMap = photometricInterpretation is 3; |
472 bool isBiLevel = photometricInterpretation is 0 || photometricInterpretation is 1; | 476 bool isBiLevel = photometricInterpretation is 0 || photometricInterpretation is 1; |
473 | 477 |
474 int imageWidth = image.width; | 478 int imageWidth = image.width; |
475 int imageLength = image.height; | 479 int imageLength = image.height; |
476 int rowByteSize = image.bytesPerLine; | 480 int rowByteSize = image.bytesPerLine; |
477 | 481 |
478 int numberEntries = isBiLevel ? 9 : 11; | 482 int numberEntries = isBiLevel ? 9 : 11; |
479 int lengthDirectory = 2 + 12 * numberEntries + 4; | 483 int lengthDirectory = 2 + 12 * numberEntries + 4; |
480 /* Offset following the header and the directory */ | 484 /* Offset following the header and the directory */ |
481 int nextOffset = 8 + lengthDirectory; | 485 int nextOffset = 8 + lengthDirectory; |
482 | 486 |
483 /* Extra space used by XResolution and YResolution values */ | 487 /* Extra space used by XResolution and YResolution values */ |
484 int extraBytes = 16; | 488 int extraBytes = 16; |
485 | 489 |
486 int[] colorMap = null; | 490 int[] colorMap = null; |
487 if (isColorMap) { | 491 if (isColorMap) { |
488 PaletteData palette = image.palette; | 492 PaletteData palette = image.palette; |
489 RGB[] rgbs = palette.getRGBs(); | 493 RGB[] rgbs = palette.getRGBs(); |
490 colorMap = formatColorMap(rgbs); | 494 colorMap = formatColorMap(rgbs); |
491 /* The number of entries of the Color Map must match the bitsPerSample field */ | 495 /* The number of entries of the Color Map must match the bitsPerSample field */ |
492 if (colorMap.length !is 3 * 1 << image.depth) DWT.error(DWT.ERROR_UNSUPPORTED_FORMAT); | 496 if (colorMap.length !is 3 * 1 << image.depth) DWT.error(DWT.ERROR_UNSUPPORTED_FORMAT); |
494 extraBytes += colorMap.length * 2; | 498 extraBytes += colorMap.length * 2; |
495 } | 499 } |
496 if (isRGB) { | 500 if (isRGB) { |
497 /* Extra space used by BitsPerSample values */ | 501 /* Extra space used by BitsPerSample values */ |
498 extraBytes += 6; | 502 extraBytes += 6; |
499 } | 503 } |
500 /* TIFF recommends storing the data in strips of no more than 8 Ko */ | 504 /* TIFF recommends storing the data in strips of no more than 8 Ko */ |
501 byte[] data = image.data; | 505 byte[] data = image.data; |
502 int[][] strips = new int[2][]; | 506 int[][] strips = new int[][](2); |
503 int nbrRowsPerStrip = formatStrips(rowByteSize, imageLength, data, 8192, nextOffset, extraBytes, strips); | 507 int nbrRowsPerStrip = formatStrips(rowByteSize, imageLength, data, 8192, nextOffset, extraBytes, strips); |
504 int[] stripOffsets = strips[0]; | 508 int[] stripOffsets = strips[0]; |
505 int[] stripByteCounts = strips[1]; | 509 int[] stripByteCounts = strips[1]; |
506 | 510 |
507 int bitsPerSampleOffset = NO_VALUE; | 511 int bitsPerSampleOffset = NO_VALUE; |
526 colorMapOffset = nextOffset; | 530 colorMapOffset = nextOffset; |
527 nextOffset += colorMap.length * 2; | 531 nextOffset += colorMap.length * 2; |
528 } | 532 } |
529 /* TIFF header */ | 533 /* TIFF header */ |
530 writeHeader(); | 534 writeHeader(); |
531 | 535 |
532 /* Image File Directory */ | 536 /* Image File Directory */ |
533 out.writeShort(numberEntries); | 537 ostr.writeShort(numberEntries); |
534 writeEntry(TAG_ImageWidth, TYPE_LONG, 1, imageWidth); | 538 writeEntry(TAG_ImageWidth, TYPE_LONG, 1, imageWidth); |
535 writeEntry(TAG_ImageLength, TYPE_LONG, 1, imageLength); | 539 writeEntry(TAG_ImageLength, TYPE_LONG, 1, imageLength); |
536 if (isColorMap) writeEntry(TAG_BitsPerSample, TYPE_SHORT, 1, image.depth); | 540 if (isColorMap) writeEntry(TAG_BitsPerSample, TYPE_SHORT, 1, image.depth); |
537 if (isRGB) writeEntry(TAG_BitsPerSample, TYPE_SHORT, 3, bitsPerSampleOffset); | 541 if (isRGB) writeEntry(TAG_BitsPerSample, TYPE_SHORT, 3, bitsPerSampleOffset); |
538 writeEntry(TAG_Compression, TYPE_SHORT, 1, COMPRESSION_NONE); | 542 writeEntry(TAG_Compression, TYPE_SHORT, 1, COMPRESSION_NONE); |
539 writeEntry(TAG_PhotometricInterpretation, TYPE_SHORT, 1, photometricInterpretation); | 543 writeEntry(TAG_PhotometricInterpretation, TYPE_SHORT, 1, photometricInterpretation); |
543 writeEntry(TAG_StripByteCounts, TYPE_LONG, cnt, cnt > 1 ? stripByteCountsOffset : stripByteCounts[0]); | 547 writeEntry(TAG_StripByteCounts, TYPE_LONG, cnt, cnt > 1 ? stripByteCountsOffset : stripByteCounts[0]); |
544 writeEntry(TAG_XResolution, TYPE_RATIONAL, 1, xResolutionOffset); | 548 writeEntry(TAG_XResolution, TYPE_RATIONAL, 1, xResolutionOffset); |
545 writeEntry(TAG_YResolution, TYPE_RATIONAL, 1, yResolutionOffset); | 549 writeEntry(TAG_YResolution, TYPE_RATIONAL, 1, yResolutionOffset); |
546 if (isColorMap) writeEntry(TAG_ColorMap, TYPE_SHORT, colorMap.length, colorMapOffset); | 550 if (isColorMap) writeEntry(TAG_ColorMap, TYPE_SHORT, colorMap.length, colorMapOffset); |
547 /* Offset of next IFD (0 for last IFD) */ | 551 /* Offset of next IFD (0 for last IFD) */ |
548 out.writeInt(0); | 552 ostr.writeInt(0); |
549 | 553 |
550 /* Values longer than 4 bytes Section */ | 554 /* Values longer than 4 bytes Section */ |
551 | 555 |
552 /* BitsPerSample 8,8,8 */ | 556 /* BitsPerSample 8,8,8 */ |
553 if (isRGB) for (int i = 0; i < 3; i++) out.writeShort(8); | 557 if (isRGB) for (int i = 0; i < 3; i++) ostr.writeShort(8); |
554 if (cnt > 1) { | 558 if (cnt > 1) { |
555 for (int i = 0; i < cnt; i++) out.writeInt(stripOffsets[i]); | 559 for (int i = 0; i < cnt; i++) ostr.writeInt(stripOffsets[i]); |
556 for (int i = 0; i < cnt; i++) out.writeInt(stripByteCounts[i]); | 560 for (int i = 0; i < cnt; i++) ostr.writeInt(stripByteCounts[i]); |
557 } | 561 } |
558 /* XResolution and YResolution set to 300 dpi */ | 562 /* XResolution and YResolution set to 300 dpi */ |
559 for (int i = 0; i < 2; i++) { | 563 for (int i = 0; i < 2; i++) { |
560 out.writeInt(300); | 564 ostr.writeInt(300); |
561 out.writeInt(1); | 565 ostr.writeInt(1); |
562 } | 566 } |
563 /* ColorMap */ | 567 /* ColorMap */ |
564 if (isColorMap) for (int i = 0; i < colorMap.length; i++) out.writeShort(colorMap[i]); | 568 if (isColorMap) for (int i = 0; i < colorMap.length; i++) ostr.writeShort(colorMap[i]); |
565 | 569 |
566 /* Image Data */ | 570 /* Image Data */ |
567 out.write(data); | 571 ostr.write(data); |
568 } | 572 } |
569 | 573 |
570 void writeEntry(short tag, int type, int count, int value) { | 574 void writeEntry(short tag, int type, int count, int value) { |
571 out.writeShort(tag); | 575 ostr.writeShort(tag); |
572 out.writeShort(type); | 576 ostr.writeShort(type); |
573 out.writeInt(count); | 577 ostr.writeInt(count); |
574 out.writeInt(value); | 578 ostr.writeInt(value); |
575 } | 579 } |
576 | 580 |
577 void writeHeader() { | 581 void writeHeader() { |
578 /* little endian */ | 582 /* little endian */ |
579 out.write(0x49); | 583 ostr.write(0x49); |
580 out.write(0x49); | 584 ostr.write(0x49); |
581 | 585 |
582 /* TIFF identifier */ | 586 /* TIFF identifier */ |
583 out.writeShort(42); | 587 ostr.writeShort(42); |
584 /* | 588 /* |
585 * Offset of the first IFD is chosen to be 8. | 589 * Offset of the first IFD is chosen to be 8. |
586 * It is word aligned and immediately after this header. | 590 * It is word aligned and immediately after this header. |
587 */ | 591 */ |
588 out.writeInt(8); | 592 ostr.writeInt(8); |
589 } | 593 } |
590 | 594 |
591 void writeToStream(LEDataOutputStream byteStream) { | 595 void writeToStream(LEDataOutputStream byteStream) { |
592 out = byteStream; | 596 ostr = byteStream; |
593 int photometricInterpretation = -1; | 597 int photometricInterpretation = -1; |
594 | 598 |
595 /* Scanline pad must be 1 */ | 599 /* Scanline pad must be 1 */ |
596 if (image.scanlinePad !is 1) DWT.error(DWT.ERROR_UNSUPPORTED_FORMAT); | 600 if (image.scanlinePad !is 1) DWT.error(DWT.ERROR_UNSUPPORTED_FORMAT); |
597 switch (image.depth) { | 601 switch (image.depth) { |
598 case 1: { | 602 case 1: { |
599 /* Palette must be black and white or white and black */ | 603 /* Palette must be black and white or white and black */ |
603 RGB rgb0 = rgbs[0]; | 607 RGB rgb0 = rgbs[0]; |
604 RGB rgb1 = rgbs[1]; | 608 RGB rgb1 = rgbs[1]; |
605 if (!(rgb0.red is rgb0.green && rgb0.green is rgb0.blue && | 609 if (!(rgb0.red is rgb0.green && rgb0.green is rgb0.blue && |
606 rgb1.red is rgb1.green && rgb1.green is rgb1.blue && | 610 rgb1.red is rgb1.green && rgb1.green is rgb1.blue && |
607 ((rgb0.red is 0x0 && rgb1.red is 0xFF) || (rgb0.red is 0xFF && rgb1.red is 0x0)))) { | 611 ((rgb0.red is 0x0 && rgb1.red is 0xFF) || (rgb0.red is 0xFF && rgb1.red is 0x0)))) { |
608 DWT.error(DWT.ERROR_UNSUPPORTED_FORMAT); | 612 DWT.error(DWT.ERROR_UNSUPPORTED_FORMAT); |
609 } | 613 } |
610 /* 0 means a color index of 0 is imaged as white */ | 614 /* 0 means a color index of 0 is imaged as white */ |
611 photometricInterpretation = image.palette.colors[0].red is 0xFF ? 0 : 1; | 615 photometricInterpretation = image.palette.colors[0].red is 0xFF ? 0 : 1; |
612 break; | 616 break; |
613 } | 617 } |