001: package org.libtiff.jai.codecimpl;
002:
003: /*
004: * XTIFF: eXtensible TIFF libraries for JAI.
005: *
006: * The contents of this file are subject to the JAVA ADVANCED IMAGING SAMPLE
007: * INPUT-OUTPUT CODECS AND WIDGET HANDLING SOURCE CODE License Version 1.0 (the
008: * "License"); You may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at
010: * http://www.sun.com/software/imaging/JAI/index.html
011: *
012: * Software distributed under the License is distributed on an "AS IS" basis,
013: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
014: * the specific language governing rights and limitations under the License.
015: *
016: * The Original Code is JAVA ADVANCED IMAGING SAMPLE INPUT-OUTPUT CODECS AND
017: * WIDGET HANDLING SOURCE CODE. The Initial Developer of the Original Code is:
018: * Sun Microsystems, Inc.. Portions created by: Niles Ritter are Copyright (C):
019: * Niles Ritter, GeoTIFF.org, 1999,2000. All Rights Reserved. Contributor(s):
020: * Niles Ritter
021: */
022:
023: import java.awt.Rectangle;
024: import java.awt.color.ColorSpace;
025: import java.awt.image.ColorModel;
026: import java.awt.image.DataBuffer;
027: import java.awt.image.IndexColorModel;
028: import java.awt.image.RenderedImage;
029: import java.awt.image.SampleModel;
030: import java.io.File;
031: import java.io.FileInputStream;
032: import java.io.FileOutputStream;
033: import java.io.IOException;
034: import java.io.OutputStream;
035: import java.util.Vector;
036:
037: import org.libtiff.jai.codec.XTIFF;
038: import org.libtiff.jai.codec.XTIFFDirectory;
039: import org.libtiff.jai.codec.XTIFFEncodeParam;
040: import org.libtiff.jai.codec.XTIFFField;
041: import org.libtiff.jai.codec.XTIFFTileCodec;
042: import org.libtiff.jai.util.JaiI18N;
043:
044: import com.sun.media.jai.codec.ImageEncodeParam;
045: import com.sun.media.jai.codec.TIFFEncodeParam;
046: import com.sun.media.jai.codec.TIFFField;
047: import com.sun.media.jai.codecimpl.TIFFImageDecoder;
048: import com.sun.media.jai.codecimpl.TIFFImageEncoder;
049:
050: /**
051: * A baseline TIFF writer. The writer outputs TIFF images in either Bilevel,
052: * Greyscale, Palette color or Full Color modes.
053: *
054: */
055: public class XTIFFImageEncoder extends TIFFImageEncoder {
056:
057: long firstIFDOffset = 0;
058: XTIFFDirectory directory;
059: XTIFFEncodeParam tparam;
060: int width;
061: int length;
062: SampleModel sampleModel;
063: int numBands;
064: int sampleSize[];
065: int dataType;
066: boolean dataTypeIsShort;
067: ColorModel colorModel;
068: int numTiles;
069: int compression;
070: boolean isTiled;
071: long tileLength;
072: long tileWidth;
073: byte[] bpixels = null;
074: long stripTileByteCounts[];
075: long stripTileOffsets[];
076: long currentOffset = 0;
077:
078: // Image Types
079: public static final int XTIFF_BILEVEL_WHITE_IS_ZERO = 0;
080: public static final int XTIFF_BILEVEL_BLACK_IS_ZERO = 1;
081: public static final int XTIFF_PALETTE = 2;
082: public static final int XTIFF_FULLCOLOR = 3;
083: public static final int XTIFF_GREYSCALE = 4;
084:
085: /**
086: * Standard constructor
087: */
088: public XTIFFImageEncoder(OutputStream output, ImageEncodeParam param) {
089: super (output, param);
090: if (this .param == null || !(param instanceof XTIFFEncodeParam)) {
091: this .param = new XTIFFEncodeParam((TIFFEncodeParam) param);
092: }
093: tparam = (XTIFFEncodeParam) this .param;
094: directory = tparam.getDirectory();
095: }
096:
097: private File createTemp() throws IOException {
098: String tmpdir = System.getProperty("tiff.io.tmpdir");
099: File file = null;
100: if (tmpdir != null)
101: file = File.createTempFile("libtiff.jai.", ".dat",
102: new File(tmpdir));
103: else
104: file = File.createTempFile("libtiff.jai.", ".dat");
105: file.deleteOnExit();
106: return file;
107: }
108:
109: private void copyImageData(File tmp, OutputStream out, int total)
110: throws IOException {
111: int bufsize = 1024;
112: int bytes = 0;
113: byte[] buf = new byte[bufsize];
114: FileInputStream in = new FileInputStream(tmp);
115: do {
116: bytes = in.read(buf);
117: out.write(buf, 0, bytes);
118: total -= bytes;
119: } while (total > 0);
120: in.close();
121: }
122:
123: /**
124: * Encodes a RenderedImage and writes the output to the OutputStream
125: * associated with this ImageEncoder.
126: */
127: public void encode(RenderedImage im) throws IOException {
128:
129: // Set comp into directory
130: compression = tparam.getCompression();
131:
132: // see if tiled
133: isTiled = ((TIFFEncodeParam) param).getWriteTiled();
134:
135: // Setup Directory fields.
136: getImageFields(im);
137:
138: if (compression == XTIFF.COMPRESSION_NONE) {
139: computeIFDOffset();
140: writeFileHeader(firstIFDOffset);
141: currentOffset = 8;
142: writeImageData(im, output);
143: writeDirectory(directory.getFields(), 0);
144: } else {
145: // We have to write compressed data out to
146: // a temp file to compute the IFD offset.
147: // The only alternative is to compress the
148: // data twice, which is just about as bad.
149: currentOffset = 8;
150: File tmp = null;
151: try {
152: tmp = createTemp();
153: OutputStream tmpOut = new FileOutputStream(tmp);
154: int total = writeImageData(im, tmpOut);
155: tmpOut.close();
156: writeFileHeader(currentOffset + currentOffset % 2);
157: copyImageData(tmp, output, total);
158: writeDirectory(directory.getFields(), 0);
159: } finally {
160: if (tmp != null)
161: tmp.delete();
162: }
163: }
164: }
165:
166: /**
167: * Precomputes the IFD Offset for uncompressed data.
168: */
169: private void computeIFDOffset() {
170:
171: long bytesPerRow = (long) Math.ceil((sampleSize[0] / 8.0)
172: * tileWidth * numBands);
173: long bytesPerTile = bytesPerRow * tileLength;
174: long lastTile = bytesPerTile;
175:
176: if (!isTiled) {
177: // Last strip may have lesser rows
178: long lastStripRows = length - (tileLength * (numTiles - 1));
179: lastTile = lastStripRows * bytesPerRow;
180: }
181:
182: long totalBytesOfData = bytesPerTile * (numTiles - 1)
183: + lastTile;
184:
185: // File header always occupies 8 bytes and we write the image data
186: // after that.
187: firstIFDOffset = 8 + totalBytesOfData;
188: // Must begin on a word boundary
189: if ((firstIFDOffset % 2) != 0) {
190: firstIFDOffset++;
191: }
192: }
193:
194: private void writeFileHeader(long firstIFDOffset)
195: throws IOException {
196: // 8 byte image file header
197:
198: // Byte order used within the file - Big Endian
199: output.write('M');
200: output.write('M');
201:
202: // Magic value
203: output.write(0);
204: output.write(42);
205:
206: // Offset in bytes of the first IFD, must begin on a word boundary
207: writeLong(firstIFDOffset);
208:
209: }
210:
211: // method for adding tags that haven't been set by user
212: private void addIfAbsent(int tag, int type, int count, Object obj) {
213: if (directory.getField(tag) == null)
214: directory.addField(tag, type, count, obj);
215: }
216:
217: private void getImageFields(RenderedImage im)
218: /* throws IOException */
219: {
220:
221: width = im.getWidth();
222: length = im.getHeight(); //TIFF calls it length
223:
224: sampleModel = im.getSampleModel();
225: numBands = sampleModel.getNumBands();
226: sampleSize = sampleModel.getSampleSize();
227:
228: dataType = sampleModel.getDataType();
229: if (dataType != DataBuffer.TYPE_BYTE
230: && dataType != DataBuffer.TYPE_SHORT
231: && dataType != DataBuffer.TYPE_USHORT) {
232: // Support only byte and (unsigned) short.
233: throw new Error(JaiI18N.getString("TIFFImageEncoder0"));
234: }
235:
236: dataTypeIsShort = dataType == DataBuffer.TYPE_SHORT
237: || dataType == DataBuffer.TYPE_USHORT;
238:
239: colorModel = im.getColorModel();
240: if (colorModel != null && colorModel instanceof IndexColorModel
241: && dataTypeIsShort) {
242: // Don't support (unsigned) short palette-color images.
243: throw new Error(JaiI18N.getString("TIFFImageEncoder2"));
244: }
245: IndexColorModel icm = null;
246: int sizeOfColormap = 0;
247: char colormap[] = null;
248:
249: // Basic fields - have to be in increasing numerical order BILEVEL
250: // ImageWidth 256
251: // ImageLength 257
252: // BitsPerSample 258
253: // Compression 259
254: // PhotoMetricInterpretation 262
255: // StripOffsets 273
256: // RowsPerStrip 278
257: // StripByteCounts 279
258: // XResolution 282
259: // YResolution 283
260: // ResolutionUnit 296
261:
262: int photometricInterpretation = XTIFF.PHOTOMETRIC_RGB;
263: int imageType = XTIFF_FULLCOLOR;
264:
265: // IMAGE TYPES POSSIBLE
266:
267: // Bilevel
268: // BitsPerSample = 1
269: // Compression = 1, 2, or 32773
270: // PhotometricInterpretation either 0 or 1
271:
272: // Greyscale
273: // BitsPerSample = 4 or 8
274: // Compression = 1, 32773
275: // PhotometricInterpretation either 0 or 1
276:
277: // Palette
278: // ColorMap 320
279: // BitsPerSample = 4 or 8
280: // PhotometrciInterpretation = 3
281:
282: // Full color
283: // BitsPerSample = 8, 8, 8
284: // SamplesPerPixel = 3 or more 277
285: // Compression = 1, 32773
286: // PhotometricInterpretation = 2
287:
288: if (colorModel instanceof IndexColorModel) {
289:
290: icm = (IndexColorModel) colorModel;
291: int mapSize = icm.getMapSize();
292:
293: if (sampleSize[0] == 1) {
294: // Bilevel image
295:
296: if (mapSize != 2) {
297: throw new IllegalArgumentException(JaiI18N
298: .getString("TIFFImageEncoder1"));
299: }
300:
301: byte r[] = new byte[mapSize];
302: icm.getReds(r);
303: byte g[] = new byte[mapSize];
304: icm.getGreens(g);
305: byte b[] = new byte[mapSize];
306: icm.getBlues(b);
307:
308: if ((r[0] & 0xff) == 0 && (r[1] & 0xff) == 255
309: && (g[0] & 0xff) == 0 && (g[1] & 0xff) == 255
310: && (b[0] & 0xff) == 0 && (b[1] & 0xff) == 255) {
311:
312: imageType = XTIFF_BILEVEL_BLACK_IS_ZERO;
313:
314: } else if ((r[0] & 0xff) == 255 && (r[1] & 0xff) == 0
315: && (g[0] & 0xff) == 255 && (g[1] & 0xff) == 0
316: && (b[0] & 0xff) == 255 && (b[1] & 0xff) == 0) {
317:
318: imageType = XTIFF_BILEVEL_WHITE_IS_ZERO;
319:
320: } else {
321: imageType = XTIFF_PALETTE;
322: }
323:
324: } else {
325: // Palette color image.
326: imageType = XTIFF_PALETTE;
327: }
328: } else {
329:
330: // If it is not an IndexColorModel, it can either be a greyscale
331: // image or a full color image
332:
333: if ((colorModel == null || colorModel.getColorSpace()
334: .getType() == ColorSpace.TYPE_GRAY)
335: && numBands == 1) {
336: // Greyscale image
337: imageType = XTIFF_GREYSCALE;
338: } else {
339: // Full color image
340: imageType = XTIFF_FULLCOLOR;
341: }
342: }
343:
344: switch (imageType) {
345:
346: case XTIFF_BILEVEL_WHITE_IS_ZERO:
347: photometricInterpretation = XTIFF.PHOTOMETRIC_WHITE_IS_ZERO;
348: break;
349:
350: case XTIFF_BILEVEL_BLACK_IS_ZERO:
351: photometricInterpretation = XTIFF.PHOTOMETRIC_BLACK_IS_ZERO;
352: break;
353:
354: case XTIFF_GREYSCALE:
355: // Since the CS_GRAY colorspace is always of type black_is_zero
356: photometricInterpretation = XTIFF.PHOTOMETRIC_BLACK_IS_ZERO;
357: break;
358:
359: case XTIFF_PALETTE:
360: photometricInterpretation = XTIFF.PHOTOMETRIC_PALETTE;
361:
362: icm = (IndexColorModel) colorModel;
363: sizeOfColormap = icm.getMapSize();
364:
365: byte r[] = new byte[sizeOfColormap];
366: icm.getReds(r);
367: byte g[] = new byte[sizeOfColormap];
368: icm.getGreens(g);
369: byte b[] = new byte[sizeOfColormap];
370: icm.getBlues(b);
371:
372: int redIndex = 0,
373: greenIndex = sizeOfColormap;
374: int blueIndex = 2 * sizeOfColormap;
375: colormap = new char[sizeOfColormap * 3];
376: for (int i = 0; i < sizeOfColormap; i++) {
377: colormap[redIndex++] = (char) (r[i] << 8);
378: colormap[greenIndex++] = (char) (g[i] << 8);
379: colormap[blueIndex++] = (char) (b[i] << 8);
380: }
381:
382: sizeOfColormap *= 3;
383:
384: // Since we will be writing the colormap field.
385: break;
386:
387: case XTIFF_FULLCOLOR:
388: photometricInterpretation = XTIFF.PHOTOMETRIC_RGB;
389: break;
390:
391: }
392:
393: if (isTiled) {
394: tileWidth = 16L;
395: tileLength = 16L;
396: XTIFFField fld = directory
397: .getField(XTIFF.TIFFTAG_TILE_WIDTH);
398: if (fld != null)
399: tileWidth = (int) fld.getAsLong(0);
400: fld = directory.getField(XTIFF.TIFFTAG_TILE_LENGTH);
401: if (fld != null)
402: tileLength = (int) fld.getAsLong(0);
403: } else {
404: // Default strip is 8 rows.
405: tileLength = 8L;
406: // tileWidth of strip is width
407:
408: tileWidth = width;
409: XTIFFField fld = directory
410: .getField(TIFFImageDecoder.TIFF_ROWS_PER_STRIP);
411: if (fld != null)
412: tileLength = fld.getAsLong(0);
413: }
414:
415: numTiles = (int) Math.ceil((double) length
416: / (double) tileLength)
417: * (int) Math.ceil((double) width / (double) tileWidth);
418:
419: stripTileByteCounts = new long[numTiles];
420: stripTileOffsets = new long[numTiles];
421:
422: // Image Width
423: directory
424: .addField(XTIFF.TIFFTAG_IMAGE_WIDTH,
425: TIFFField.TIFF_LONG, 1,
426: (Object) (new long[] { width }));
427:
428: // Image Length
429: directory.addField(XTIFF.TIFFTAG_IMAGE_LENGTH,
430: TIFFField.TIFF_LONG, 1, new long[] { length });
431:
432: directory.addField(XTIFF.TIFFTAG_BITS_PER_SAMPLE,
433: TIFFField.TIFF_SHORT, numBands,
434: convertToChars(sampleSize));
435:
436: directory.addField(XTIFF.TIFFTAG_COMPRESSION,
437: TIFFField.TIFF_SHORT, 1,
438: new char[] { (char) compression });
439:
440: directory.addField(XTIFF.TIFFTAG_PHOTOMETRIC_INTERPRETATION,
441: TIFFField.TIFF_SHORT, 1,
442: new char[] { (char) photometricInterpretation });
443:
444: directory
445: .addField(XTIFF.TIFFTAG_SAMPLES_PER_PIXEL,
446: TIFFField.TIFF_SHORT, 1,
447: new char[] { (char) numBands });
448:
449: if (isTiled) {
450: directory.addField(XTIFF.TIFFTAG_TILE_WIDTH,
451: TIFFField.TIFF_LONG, 1, new long[] { tileWidth });
452:
453: directory.addField(XTIFF.TIFFTAG_TILE_LENGTH,
454: TIFFField.TIFF_LONG, 1, new long[] { tileLength });
455:
456: directory.addField(XTIFF.TIFFTAG_TILE_OFFSETS,
457: TIFFField.TIFF_LONG, numTiles, stripTileOffsets);
458:
459: directory.addField(XTIFF.TIFFTAG_TILE_BYTE_COUNTS,
460: TIFFField.TIFF_LONG, numTiles, stripTileByteCounts);
461: } else {
462: directory.addField(XTIFF.TIFFTAG_STRIP_OFFSETS,
463: TIFFField.TIFF_LONG, numTiles, stripTileOffsets);
464:
465: directory.addField(XTIFF.TIFFTAG_ROWS_PER_STRIP,
466: TIFFField.TIFF_LONG, 1, new long[] { tileLength });
467:
468: directory.addField(XTIFF.TIFFTAG_STRIP_BYTE_COUNTS,
469: TIFFField.TIFF_LONG, numTiles, stripTileByteCounts);
470: }
471:
472: addIfAbsent(XTIFF.TIFFTAG_X_RESOLUTION,
473: TIFFField.TIFF_RATIONAL, 1, new long[][] { { 72, 1 } });
474:
475: addIfAbsent(XTIFF.TIFFTAG_Y_RESOLUTION,
476: TIFFField.TIFF_RATIONAL, 1, new long[][] { { 72, 1 } });
477:
478: addIfAbsent(XTIFF.TIFFTAG_RESOLUTION_UNIT,
479: TIFFField.TIFF_SHORT, 1, new char[] { (char) 2 });
480:
481: if (colormap != null) {
482: directory.addField(XTIFF.TIFFTAG_COLORMAP,
483: TIFFField.TIFF_SHORT, sizeOfColormap, colormap);
484: }
485:
486: // Data Sample Format Extension fields.
487: if (dataTypeIsShort) {
488: // SampleFormat
489: int[] sampleFormat = new int[numBands];
490: sampleFormat[0] = dataType == DataBuffer.TYPE_USHORT ? 1
491: : 2;
492: for (int b = 1; b < numBands; b++) {
493: sampleFormat[b] = sampleFormat[0];
494: }
495: directory.addField(XTIFF.TIFFTAG_SAMPLE_FORMAT,
496: TIFFField.TIFF_SHORT, numBands,
497: convertToChars(sampleFormat));
498:
499: // SMinSampleValue: set to data type minimum.
500: int[] minValue = new int[numBands];
501: minValue[0] = dataType == DataBuffer.TYPE_USHORT ? 0
502: : Short.MIN_VALUE;
503: for (int b = 1; b < numBands; b++) {
504: minValue[b] = minValue[0];
505: }
506: directory.addField(XTIFF.TIFFTAG_S_MIN_SAMPLE_VALUE,
507: TIFFField.TIFF_SHORT, numBands,
508: convertToChars(minValue));
509:
510: // SMaxSampleValue: set to data type maximum.
511: int[] maxValue = new int[numBands];
512: maxValue[0] = dataType == DataBuffer.TYPE_USHORT ? 65535
513: : Short.MAX_VALUE;
514: for (int b = 1; b < numBands; b++) {
515: maxValue[b] = maxValue[0];
516: }
517: directory.addField(XTIFF.TIFFTAG_S_MAX_SAMPLE_VALUE,
518: TIFFField.TIFF_SHORT, numBands,
519: convertToChars(maxValue));
520: }
521:
522: }
523:
524: private char[] convertToChars(int[] shorts) {
525: char[] out = new char[shorts.length];
526: for (int i = 0; i < shorts.length; i++)
527: out[i] = (char) shorts[i];
528: return out;
529: }
530:
531: protected int getSampleSize() {
532: if (dataType == DataBuffer.TYPE_BYTE)
533: return 1;
534: else if (dataTypeIsShort)
535: return 2;
536: return 1; // what should go here?
537: }
538:
539: protected int getTileSize() {
540: return (int) (tileLength * tileWidth * numBands);
541: }
542:
543: private int writeImageData(RenderedImage im, OutputStream out)
544: throws IOException {
545: int total = 0;
546:
547: // Get the encoder
548: XTIFFTileCodec codec = directory.createTileCodec(tparam);
549:
550: // Create a buffer to hold the data
551: // to be written to the file, so we can use array writes.
552: int tsize = codec.getCompressedTileSize(im);
553: bpixels = new byte[tsize];
554:
555: // Encode one tile at a time
556: Rectangle rect = new Rectangle();
557: float minX = (float) im.getMinX();
558: float minY = (float) im.getMinY();
559: float rows = (float) tileLength;
560: float cols = (float) tileWidth;
561: int i = 0;
562: for (int row = 0; row < length; row += tileLength) {
563: for (int col = 0; col < width; col += tileWidth) {
564: if (!isTiled)
565: rows = Math.min(tileLength, length - row);
566: rect.setRect(minX + col, minY + row, cols, rows);
567: int tileSize = codec.encode(im, rect, bpixels);
568: out.write(bpixels, 0, tileSize);
569: stripTileOffsets[i] = currentOffset;
570: stripTileByteCounts[i++] = tileSize;
571: currentOffset += tileSize;
572: total += tileSize;
573: }
574: }
575: return total;
576: }
577:
578: private void writeDirectory(XTIFFField fields[], int nextIFDOffset)
579: throws IOException {
580:
581: if (currentOffset % 2 == 1) {
582: output.write(0);
583: currentOffset++;
584: }
585:
586: // 2 byte count of number of directory entries (fields)
587: int numEntries = fields.length;
588:
589: long offsetBeyondIFD = currentOffset + 12 * numEntries + 4 + 2;
590: Vector tooBig = new Vector();
591:
592: XTIFFField field;
593: int tag;
594: int type;
595: int count;
596:
597: // Write number of fields in the IFD
598: writeUnsignedShort(numEntries);
599:
600: for (int i = 0; i < numEntries; i++) {
601:
602: field = fields[i];
603:
604: // 12 byte field entry TIFFField
605:
606: // byte 0-1 Tag that identifies a field
607: tag = field.getTag();
608: writeUnsignedShort(tag);
609:
610: // byte 2-3 The field type
611: type = field.getType();
612: writeUnsignedShort(type);
613:
614: // bytes 4-7 the number of values of the indicated type
615: count = field.getCount();
616: writeLong(count);
617:
618: // bytes 8 - 11 the value offset
619: if (count * sizeOfType[type] > 4) {
620:
621: // We need an offset as data won't fit into 4 bytes
622: writeLong(offsetBeyondIFD);
623: offsetBeyondIFD += (count * sizeOfType[type]);
624: tooBig.add(new Integer(i));
625:
626: } else {
627:
628: writeValuesAsFourBytes(field);
629: }
630:
631: }
632:
633: // Address of next IFD
634: writeLong(nextIFDOffset);
635:
636: int index;
637: // Write the tag values that did not fit into 4 bytes
638:
639: for (int i = 0; i < tooBig.size(); i++) {
640: index = ((Integer) tooBig.elementAt(i)).intValue();
641: writeValues(fields[index]);
642: }
643: }
644:
645: private static final int[] sizeOfType = { 0, // 0 = n/a
646: 1, // 1 = byte
647: 1, // 2 = ascii
648: 2, // 3 = short
649: 4, // 4 = long
650: 8, // 5 = rational
651: 1, // 6 = sbyte
652: 1, // 7 = undefined
653: 2, // 8 = sshort
654: 4, // 9 = slong
655: 8, // 10 = srational
656: 4, // 11 = float
657: 8 // 12 = double
658: };
659:
660: private void writeValuesAsFourBytes(XTIFFField field)
661: throws IOException {
662:
663: int dataType = field.getType();
664: int count = field.getCount();
665:
666: switch (dataType) {
667:
668: // unsigned 8 bits
669: case TIFFField.TIFF_BYTE:
670: byte bytes[] = field.getAsBytes();
671:
672: for (int i = 0; i < count; i++) {
673: output.write(bytes[i]);
674: }
675:
676: for (int i = 0; i < (4 - count); i++) {
677: output.write(0);
678: }
679:
680: break;
681:
682: // unsigned 16 bits
683: case TIFFField.TIFF_SHORT:
684: char shorts[] = field.getAsChars();
685:
686: for (int i = 0; i < count; i++) {
687: writeUnsignedShort((int) shorts[i]);
688: }
689:
690: for (int i = 0; i < (2 - count); i++) {
691: writeUnsignedShort(0);
692: }
693:
694: break;
695:
696: // unsigned 32 bits
697: case TIFFField.TIFF_LONG:
698: long longs[] = field.getAsLongs();
699:
700: for (int i = 0; i < count; i++) {
701: writeLong(longs[i]);
702: }
703: break;
704: }
705:
706: }
707:
708: private void writeValues(XTIFFField field) throws IOException {
709:
710: int dataType = field.getType();
711: int count = field.getCount();
712:
713: switch (dataType) {
714:
715: // character data with NULL termination
716: case TIFFField.TIFF_ASCII:
717: String strings[] = field.getAsStrings();
718: for (int i = 0; i < strings.length; i++) {
719: byte bytes[] = strings[i].getBytes();
720: for (int j = 0; j < bytes.length; j++) {
721: output.write(bytes[j]);
722: }
723: if ((i + 1) < count)
724: output.write(0);
725: }
726: break;
727:
728: // unsigned 8 bits
729: case TIFFField.TIFF_BYTE:
730: byte bytes[] = field.getAsBytes();
731: for (int i = 0; i < count; i++) {
732: output.write(bytes[i]);
733: }
734: break;
735:
736: // unsigned 16 bits
737: case TIFFField.TIFF_SHORT:
738: char shorts[] = field.getAsChars();
739: for (int i = 0; i < count; i++) {
740: writeUnsignedShort((int) shorts[i]);
741: }
742: break;
743:
744: // unsigned 32 bits
745: case TIFFField.TIFF_LONG:
746: long longs[] = field.getAsLongs();
747: for (int i = 0; i < count; i++) {
748: writeLong(longs[i]);
749: }
750: break;
751:
752: // IEEE 8-byte double
753: case TIFFField.TIFF_DOUBLE:
754: double doubles[] = field.getAsDoubles();
755: for (int i = 0; i < count; i++) {
756: writeDouble(doubles[i]);
757: }
758: break;
759:
760: case TIFFField.TIFF_RATIONAL:
761: long rationals[][] = field.getAsRationals();
762: for (int i = 0; i < count; i++) {
763: writeLong(rationals[i][0]);
764: writeLong(rationals[i][1]);
765: }
766: break;
767:
768: }
769:
770: }
771:
772: // Here s is never expected to have value greater than what can be
773: // stored in 2 bytes.
774: private void writeUnsignedShort(int s) throws IOException {
775: output.write((s & 0xff00) >>> 8);
776: output.write(s & 0x00ff);
777: }
778:
779: private void writeLong(long l) throws IOException {
780: output.write((int) ((l & 0xff000000) >>> 24));
781: output.write((int) ((l & 0x00ff0000) >>> 16));
782: output.write((int) ((l & 0x0000ff00) >>> 8));
783: output.write(((int) l & 0x000000ff));
784: }
785:
786: // write 8-byte IEEE double
787: private void writeDouble(double d) throws IOException {
788: long lval = Double.doubleToLongBits(d);
789: writeLong(lval >>> 32);
790: writeLong((lval & 0xffffffff));
791: }
792:
793: }
|