Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
TiffEncoder.java
1 package ij.io;
2 import java.io.*;
3 
5 public class TiffEncoder {
6  static final int IMAGE_START = 768;
7  static final int HDR_SIZE = 8;
8  static final int MAP_SIZE = 768; // in 16-bit words
9  static final int BPS_DATA_SIZE = 6;
10  static final int SCALE_DATA_SIZE = 16;
11 
12 
13  private FileInfo fi;
14  private int bitsPerSample;
15  private int photoInterp;
16  private int samplesPerPixel;
17  private int nEntries;
18  private int ifdSize;
19  private int imageOffset = IMAGE_START;
20  private int imageSize;
21  private int stackSize;
22  private byte[] description;
23 
24  public TiffEncoder (FileInfo fi) {
25  this.fi = fi;
26  fi.intelByteOrder = false;
27  bitsPerSample = 8;
28  samplesPerPixel = 1;
29  nEntries = 9;
30  int bytesPerPixel = 1;
31  switch (fi.fileType) {
32  case FileInfo.GRAY8:
33  photoInterp = fi.whiteIsZero?0:1;
34  break;
37  bitsPerSample = 16;
38  photoInterp = fi.whiteIsZero?0:1;
39  bytesPerPixel = 2;
40  break;
42  bitsPerSample = 32;
43  photoInterp = fi.whiteIsZero?0:1;
44  bytesPerPixel = 4;
45  break;
46  case FileInfo.RGB:
47  photoInterp = 2;
48  samplesPerPixel = 3;
49  bytesPerPixel = 3;
50  break;
51  case FileInfo.COLOR8:
52  photoInterp = 3;
53  nEntries = 10;
54  break;
55  default:
56  photoInterp = 0;
57  }
58  if (fi.unit!=null && fi.pixelWidth!=0 && fi.pixelHeight!=0)
59  nEntries += 3; // XResolution, YResolution and ResolutionUnit
60  if (fi.fileType==fi.GRAY32_FLOAT)
61  nEntries++; // SampleFormat tag
62  makeDescriptionString();
63  if (description!=null)
64  nEntries++; // ImageDescription tag
65  ifdSize = 2 + nEntries*12 + 4;
66  imageSize = fi.width*fi.height*bytesPerPixel;
67  stackSize = imageSize*fi.nImages;
68  }
69 
73  public void write(DataOutputStream out) throws IOException {
74  writeHeader(out);
75  int nextIFD = 0;
76  if (fi.nImages>1) {
77  nextIFD = IMAGE_START+stackSize;
78  if (fi.fileType==FileInfo.COLOR8) nextIFD += MAP_SIZE*2;
79  }
80  writeIFD(out, imageOffset, nextIFD);
81  int bpsSize=0, scaleSize=0, descriptionSize=0;
82  if (fi.fileType==FileInfo.RGB)
83  bpsSize = writeBitsPerPixel(out);
84  if (description!=null)
85  descriptionSize = writeDescription(out);
86  if (fi.unit!=null && fi.pixelWidth!=0 && fi.pixelHeight!=0)
87  scaleSize = writeScale(out);
88  byte[] filler = new byte[IMAGE_START - (HDR_SIZE+ifdSize+bpsSize+descriptionSize+scaleSize)];
89  out.write(filler); // force image to start at offset 768
90  //ij.IJ.write("filler: "+filler.length);
91  new ImageWriter(fi).write(out);
92  if (fi.fileType==FileInfo.COLOR8)
93  writeColorMap(out);
94  for (int i=2; i<=fi.nImages; i++) {
95  if (i==fi.nImages)
96  nextIFD = 0;
97  else
98  nextIFD += ifdSize;
99  imageOffset += imageSize;
100  writeIFD(out, imageOffset, nextIFD);
101  }
102  }
103 
105  void writeHeader(DataOutputStream out) throws IOException {
106  byte[] hdr = new byte[8];
107  hdr[0] = 77; // "MM" (Motorola byte order)
108  hdr[1] = 77;
109  hdr[2] = 0; // 42 (magic number)
110  hdr[3] = 42;
111  hdr[4] = 0; // 8 (offset to first IFD)
112  hdr[5] = 0;
113  hdr[6] = 0;
114  hdr[7] = 8;
115  out.write(hdr);
116  }
117 
119  void writeEntry(DataOutputStream out, int tag, int fieldType, int count, int value) throws IOException {
120  out.writeShort(tag);
121  out.writeShort(fieldType);
122  out.writeInt(count);
123  if (count==1 && fieldType==TiffDecoder.SHORT)
124  value <<= 16; //left justify 16-bit values
125  out.writeInt(value); // may be an offset
126  }
127 
129  void writeIFD(DataOutputStream out, int imageOffset, int nextIFD) throws IOException {
130  int tagDataOffset = HDR_SIZE + ifdSize;
131  out.writeShort(nEntries);
132  writeEntry(out, TiffDecoder.NEW_SUBFILE_TYPE, 4, 1, 0);
133  writeEntry(out, TiffDecoder.IMAGE_WIDTH, 3, 1, fi.width);
134  writeEntry(out, TiffDecoder.IMAGE_LENGTH, 3, 1, fi.height);
135  if (fi.fileType==FileInfo.RGB) {
136  writeEntry(out, TiffDecoder.BITS_PER_SAMPLE, 3, 3, tagDataOffset);
137  tagDataOffset += BPS_DATA_SIZE;
138  } else
139  writeEntry(out, TiffDecoder.BITS_PER_SAMPLE, 3, 1, bitsPerSample);
140  writeEntry(out, TiffDecoder.PHOTO_INTERP, 3, 1, photoInterp);
141  if (description!=null) {
142  writeEntry(out, TiffDecoder.IMAGE_DESCRIPTION, 2, description.length, tagDataOffset);
143  tagDataOffset += description.length;
144  }
145  writeEntry(out, TiffDecoder.STRIP_OFFSETS, 4, 1, imageOffset);
146  writeEntry(out, TiffDecoder.SAMPLES_PER_PIXEL,3, 1, samplesPerPixel);
147  writeEntry(out, TiffDecoder.ROWS_PER_STRIP, 3, 1, fi.height);
148  writeEntry(out, TiffDecoder.STRIP_BYTE_COUNT, 4, 1, imageSize);
149  if (fi.unit!=null && fi.pixelWidth!=0 && fi.pixelHeight!=0) {
150  writeEntry(out, TiffDecoder.X_RESOLUTION, 5, 1, tagDataOffset);
151  writeEntry(out, TiffDecoder.Y_RESOLUTION, 5, 1, tagDataOffset+8);
152  tagDataOffset += SCALE_DATA_SIZE;
153  int unit = 1;
154  if (fi.unit.equals("inch"))
155  unit = 2;
156  else if (fi.unit.equals("cm"))
157  unit = 3;
158  writeEntry(out, TiffDecoder.RESOLUTION_UNIT, 3, 1, unit);
159  }
160  if (fi.fileType==fi.GRAY32_FLOAT) {
161  int format = TiffDecoder.FLOATING_POINT;
162  writeEntry(out, TiffDecoder.SAMPLE_FORMAT, 3, 1, format);
163  }
164  if (fi.fileType==FileInfo.COLOR8)
165  writeEntry(out, TiffDecoder.COLOR_MAP, 3, MAP_SIZE, IMAGE_START+stackSize);
166  out.writeInt(nextIFD);
167  }
168 
170  int writeBitsPerPixel(DataOutputStream out) throws IOException {
171  out.writeShort(8);
172  out.writeShort(8);
173  out.writeShort(8);
174  return BPS_DATA_SIZE;
175  }
176 
178  int writeScale(DataOutputStream out) throws IOException {
179  double xscale = 1.0/fi.pixelWidth;
180  double yscale = 1.0/fi.pixelHeight;
181  double scale = 1000000.0;
182  if (xscale>1000.0) scale = 1000.0;
183  out.writeInt((int)(xscale*scale));
184  out.writeInt((int)scale);
185  out.writeInt((int)(yscale*scale));
186  out.writeInt((int)scale);
187  return SCALE_DATA_SIZE;
188  }
189 
191  int writeDescription(DataOutputStream out) throws IOException {
192  out.write(description,0,description.length);
193  return description.length;
194  }
195 
197  void writeColorMap(DataOutputStream out) throws IOException {
198  byte[] colorTable16 = new byte[MAP_SIZE*2];
199  int j=0;
200  for (int i=0; i<fi.lutSize; i++) {
201  colorTable16[j] = fi.reds[i];
202  colorTable16[512+j] = fi.greens[i];
203  colorTable16[1024+j] = fi.blues[i];
204  j += 2;
205  }
206  out.write(colorTable16);
207  }
208 
212  void makeDescriptionString() {
213  if (fi.description!=null) {
214  if (fi.description.charAt(fi.description.length()-1)!=(char)0)
215  fi.description += " ";
216  description = fi.description.getBytes();
217  description[description.length-1] = (byte)0;
218  } else
219  description = null;
220  }
221 
222 }