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 }