//
// ImarisTiffReader.java
//
/*
LOCI Bio-Formats package for reading and converting biological file formats.
Copyright (C) 2005-@year@ Melissa Linkert, Curtis Rueden, Chris Allan,
Eric Kjellman and Brian Loranger.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package loci.formats.in;
import java.io.*;
import java.util.*;
import loci.formats.*;
/**
* ImarisTiffReader is the file format reader for
* Imaris 5 files (TIFF variant).
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/loci/formats/in/ImarisTiffReader.java">Trac</a>,
* <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/loci/formats/in/ImarisTiffReader.java">SVN</a></dd></dl>
*
* @author Melissa Linkert linkert at wisc.edu
*/
public class ImarisTiffReader extends BaseTiffReader {
// -- Constructor --
/** Constructs a new Imaris TIFF reader. */
public ImarisTiffReader() {
super("Imaris 5 (TIFF)", "ims");
}
// -- IFormatReader API methods --
/* @see loci.formats.IFormatReader#isThisType(byte[]) */
public boolean isThisType(byte[] block) {
// adapted from MetamorphReader.isThisType(byte[])
if (block.length < 3) return false;
if (block.length < 8) {
return true; // we have no way of verifying further
}
boolean little = (block[0] == 0x49 && block[1] == 0x49);
int ifdlocation = DataTools.bytesToInt(block, 4, little);
if (ifdlocation < 0) return false;
else if (ifdlocation + 1 > block.length) return true;
else {
int ifdnumber = DataTools.bytesToInt(block, ifdlocation, 2, little);
for (int i=0; i<ifdnumber; i++) {
if (ifdlocation + 3 + (i*12) > block.length) {
return false;
}
else {
int ifdtag = DataTools.bytesToInt(block,
ifdlocation + 2 + (i*12), 2, little);
if (ifdtag == TiffTools.TILE_WIDTH) {
return true;
}
}
}
return false;
}
}
// -- IFormatHandler API methods --
/* @see loci.formats.IFormatHandler#isThisType(String, boolean) */
public boolean isThisType(String name, boolean open) {
if (!super.isThisType(name, open)) return false; // check extension
// just checking the filename isn't enough to differentiate between
// Andor and regular TIFF; open the file and check more thoroughly
return open ? checkBytes(name, 1024) : true;
}
// -- Internal BaseTiffReader API methods --
/* @see BaseTiffReader#initFile(String) */
protected void initFile(String id) throws FormatException, IOException {
if (debug) debug("ImarisTiffReader.initFile(" + id + ")");
super.initFile(id);
in = new RandomAccessStream(id);
if (in.readShort() == 0x4949) in.order(true);
ifds = TiffTools.getIFDs(in);
if (ifds == null) throw new FormatException("No IFDs found");
// hack up the IFDs
//
// Imaris TIFFs store a thumbnail in the first IFD; each of the remaining
// IFDs defines a stack of tiled planes.
status("Verifying IFD sanity");
Vector tmp = new Vector();
for (int i=1; i<ifds.length; i++) {
long[] byteCounts = TiffTools.getIFDLongArray(ifds[i],
TiffTools.TILE_BYTE_COUNTS, false);
long[] offsets = TiffTools.getIFDLongArray(ifds[i],
TiffTools.TILE_OFFSETS, false);
for (int j=0; j<byteCounts.length; j++) {
Hashtable t = (Hashtable) ifds[i].clone();
TiffTools.putIFDValue(t, TiffTools.TILE_BYTE_COUNTS, byteCounts[j]);
TiffTools.putIFDValue(t, TiffTools.TILE_OFFSETS, offsets[j]);
tmp.add(t);
}
}
status("Populating metadata");
core.sizeC[0] = ifds.length - 1;
core.sizeZ[0] = tmp.size() / core.sizeC[0];
core.sizeT[0] = 1;
core.sizeX[0] =
TiffTools.getIFDIntValue(ifds[1], TiffTools.IMAGE_WIDTH, false, 0);
core.sizeY[0] =
TiffTools.getIFDIntValue(ifds[1], TiffTools.IMAGE_LENGTH, false, 0);
ifds = (Hashtable[]) tmp.toArray(new Hashtable[0]);
core.imageCount[0] = core.sizeC[0] * core.sizeZ[0];
core.currentOrder[0] = "XYZCT";
core.interleaved[0] = false;
core.rgb[0] =
core.imageCount[0] != core.sizeZ[0] * core.sizeC[0] * core.sizeT[0];
int bitsPerSample = TiffTools.getIFDIntValue(ifds[0],
TiffTools.BITS_PER_SAMPLE);
int bitFormat = TiffTools.getIFDIntValue(ifds[0], TiffTools.SAMPLE_FORMAT);
while (bitsPerSample % 8 != 0) bitsPerSample++;
if (bitsPerSample == 24 || bitsPerSample == 48) bitsPerSample /= 3;
if (bitFormat == 3) core.pixelType[0] = FormatTools.FLOAT;
else if (bitFormat == 2) {
switch (bitsPerSample) {
case 8:
core.pixelType[0] = FormatTools.INT8;
break;
case 16:
core.pixelType[0] = FormatTools.INT16;
break;
case 32:
core.pixelType[0] = FormatTools.INT32;
break;
}
}
else {
switch (bitsPerSample) {
case 8:
core.pixelType[0] = FormatTools.UINT8;
break;
case 16:
core.pixelType[0] = FormatTools.UINT16;
break;
case 32:
core.pixelType[0] = FormatTools.UINT32;
break;
}
}
status("Parsing comment");
String comment = (String) getMeta("Comment");
// likely an INI-style comment, although we can't be sure
if (comment != null && comment.startsWith("[")) {
// parse key/value pairs
StringTokenizer st = new StringTokenizer(comment, "\n");
while (st.hasMoreTokens()) {
String line = st.nextToken();
int equals = line.indexOf("=");
if (equals < 0) continue;
String key = line.substring(0, equals);
String value = line.substring(equals + 1);
addMeta(key.trim(), value.trim());
}
metadata.remove("Comment");
}
MetadataStore store = getMetadataStore();
FormatTools.populatePixels(store, this);
}
}