//
// MetamorphReader.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.text.DecimalFormat;
import java.util.*;
import loci.formats.*;
/**
* Reader is the file format reader for Metamorph STK files.
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/loci/formats/in/MetamorphReader.java">Trac</a>,
* <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/loci/formats/in/MetamorphReader.java">SVN</a></dd></dl>
*
* @author Eric Kjellman egkjellman at wisc.edu
* @author Melissa Linkert linkert at wisc.edu
* @author Curtis Rueden ctrueden at wisc.edu
* @author Sebastien Huart Sebastien dot Huart at curie.fr
*/
public class MetamorphReader extends BaseTiffReader {
// -- Constants --
// IFD tag numbers of important fields
private static final int METAMORPH_ID = 33628;
private static final int UIC1TAG = METAMORPH_ID;
private static final int UIC2TAG = 33629;
private static final int UIC3TAG = 33630;
private static final int UIC4TAG = 33631;
// -- Fields --
/** The TIFF's name */
private String imageName;
/** The TIFF's creation date */
private String imageCreationDate;
//** The TIFF's emWavelength */
private long[] emWavelength;
private int mmPlanes; //number of metamorph planes
private MetamorphReader r;
/** List of STK files in the dataset. */
private String[][] stks;
// -- Constructor --
/** Constructs a new Metamorph reader. */
public MetamorphReader() {
super("Metamorph STK", new String[] {"stk", "nd"});
}
// -- IFormatReader API methods --
/* @see loci.formats.IFormatReader#isThisType(byte[]) */
public boolean isThisType(byte[] block) {
// If the file is a Metamorph STK file, it should have a specific IFD tag.
// Most Metamorph files seem to have the IFD information at the end, so it
// is difficult to determine whether or not the block is a Metamorph block
// without being passed the entire file. Therefore, we will check the only
// things we can reasonably check at the beginning of the file, and if we
// happen to be passed the entire file, well, great, we'll check that too.
// Must be little-endian TIFF
if (block.length < 3) return false;
if (block[0] != TiffTools.LITTLE) return false; // denotes little-endian
if (block[1] != TiffTools.LITTLE) return false;
if (block[2] != TiffTools.MAGIC_NUMBER) return false; // denotes TIFF
if (block.length < 8) return true; // we have no way of verifying further
int ifdlocation = DataTools.bytesToInt(block, 4, true);
if (ifdlocation + 1 > block.length) {
// we have no way of verifying this is a Metamorph file.
// It is at least a TIFF.
return true;
}
else {
int ifdnumber = DataTools.bytesToInt(block, ifdlocation, 2, true);
for (int i = 0; i < ifdnumber; i++) {
if (ifdlocation + 3 + (i * 12) > block.length) return true;
else {
int ifdtag = DataTools.bytesToInt(block,
ifdlocation + 2 + (i * 12), 2, true);
if (ifdtag == METAMORPH_ID) return true; // absolutely a valid file
}
}
return false; // we went through the IFD; the ID wasn't found.
}
}
/* @see loci.formats.IFormatReader#close() */
public void close() throws IOException {
super.close();
if (r != null) r.close();
}
/* @see loci.formats.IFormatReader#fileGroupOption(String) */
public int fileGroupOption(String id) throws FormatException, IOException {
if (id.toLowerCase().endsWith(".nd")) return FormatTools.MUST_GROUP;
Location l = new Location(id).getAbsoluteFile();
String[] files = l.getParentFile().list();
for (int i=0; i<files.length; i++) {
String s = files[i].toLowerCase();
if (s.endsWith(".nd") && id.startsWith(files[i].substring(0,
s.lastIndexOf("."))))
{
return FormatTools.CAN_GROUP;
}
}
return FormatTools.CANNOT_GROUP;
}
/* @see loci.formats.IFormatReader#getUsedFiles() */
public String[] getUsedFiles() {
return stks == null ? super.getUsedFiles() : stks[series];
}
/* @see loci.formats.IFormatReader#openBytes(int, byte[]) */
public byte[] openBytes(int no, byte[] buf)
throws FormatException, IOException
{
FormatTools.assertId(currentId, true, 1);
if (stks == null || stks[series].length == 1) {
return super.openBytes(no, buf);
}
int[] coords = FormatTools.getZCTCoords(this, no % core.sizeZ[series]);
int ndx = no / core.sizeZ[series];
String file = stks[series][ndx];
if (r == null) r = new MetamorphReader();
r.setId(file);
return r.openBytes(coords[0], buf);
}
/* @see loci.formats.FormatReader#initFile(String) */
protected void initFile(String id) throws FormatException, IOException {
if (id.toLowerCase().endsWith(".nd")) {
if (currentId != null) {
String[] s = getUsedFiles();
for (int i=0; i<s.length; i++) {
if (id.equals(s[i])) return;
}
}
close();
currentId = id;
metadata = new Hashtable();
core = new CoreMetadata(1);
Arrays.fill(core.orderCertain, true);
// reinitialize the MetadataStore
getMetadataStore().createRoot();
// find an associated STK file
String stkFile = id.substring(0, id.lastIndexOf("."));
Location parent = new Location(id).getAbsoluteFile().getParentFile();
String[] dirList = parent.list();
for (int i=0; i<dirList.length; i++) {
String s = dirList[i].toLowerCase();
if (s.endsWith(".stk") && (dirList[i].indexOf(stkFile.substring(
stkFile.lastIndexOf(File.separator) + 1) + "_w") != -1))
{
stkFile =
new Location(parent.getPath(), dirList[i]).getAbsolutePath();
break;
}
}
super.initFile(stkFile);
}
else super.initFile(id);
Location ndfile = null;
if (id.toLowerCase().endsWith(".nd")) ndfile = new Location(id);
if (ndfile != null && ndfile.exists() &&
(fileGroupOption(id) == FormatTools.MUST_GROUP || isGroupFiles()))
{
RandomAccessStream ndStream =
new RandomAccessStream(ndfile.getAbsolutePath());
String line = ndStream.readLine().trim();
while (!line.equals("\"EndFile\"")) {
String key = line.substring(1, line.indexOf(",") - 1).trim();
String value = line.substring(line.indexOf(",") + 1).trim();
addMeta(key, value);
line = ndStream.readLine().trim();
}
// figure out how many files we need
String z = (String) getMeta("NZSteps");
String c = (String) getMeta("NWavelengths");
String t = (String) getMeta("NTimePoints");
int zc = core.sizeZ[0], cc = core.sizeC[0], tc = core.sizeT[0];
if (z != null) zc = Integer.parseInt(z);
if (c != null) cc = Integer.parseInt(c);
if (t != null) tc = Integer.parseInt(t);
int numFiles = cc * tc;
// determine series count
boolean[] hasZ = new boolean[cc];
int seriesCount = 1;
for (int i=0; i<cc; i++) {
hasZ[i] = ((String) getMeta("WaveDoZ" + (i + 1))).equals("TRUE");
if (i > 0 && hasZ[i] != hasZ[i - 1]) seriesCount = 2;
}
int channelsInFirstSeries = cc;
if (seriesCount == 2) {
channelsInFirstSeries = 0;
for (int i=0; i<cc; i++) {
if (hasZ[i]) channelsInFirstSeries++;
}
}
stks = new String[seriesCount][];
if (seriesCount == 1) stks[0] = new String[numFiles];
else {
stks[0] = new String[channelsInFirstSeries * tc];
stks[1] = new String[(cc - channelsInFirstSeries) * tc];
}
String prefix = ndfile.getPath();
prefix = prefix.substring(prefix.lastIndexOf(File.separator) + 1,
prefix.lastIndexOf("."));
int[] pt = new int[seriesCount];
for (int i=0; i<tc; i++) {
for (int j=0; j<cc; j++) {
String chName = (String) getMeta("WaveName" + (j + 1));
chName = chName.substring(1, chName.length() - 1);
int seriesNdx = seriesCount == 1 ? 0 : (hasZ[j] ? 0 : 1);
stks[seriesNdx][pt[seriesNdx]++] =
prefix + "_w" + (j + 1) + chName + "_t" + (i + 1) + ".STK";
}
}
ndfile = ndfile.getAbsoluteFile();
for (int s=0; s<stks.length; s++) {
for (int f=0; f<stks[s].length; f++) {
Location l = new Location(ndfile.getParent(), stks[s][f]);
if (!l.exists()) {
// '%' can be converted to '-'
if (stks[s][f].indexOf("%") != -1) {
stks[s][f] = stks[s][f].replaceAll("%", "-");
l = new Location(ndfile.getParent(), stks[s][f]);
if (!l.exists()) {
// try replacing extension
stks[s][f] = stks[s][f].substring(0,
stks[s][f].lastIndexOf(".")) + ".TIF";
l = new Location(ndfile.getParent(), stks[s][f]);
if (!l.exists()) {
stks = null;
return;
}
}
}
if (!l.exists()) {
// try replacing extension
stks[s][f] = stks[s][f].substring(0,
stks[s][f].lastIndexOf(".")) + ".TIF";
l = new Location(ndfile.getParent(), stks[s][f]);
if (!l.exists()) {
stks = null;
return;
}
}
}
stks[s][f] = l.getAbsolutePath();
}
}
core.sizeZ[0] = zc;
core.sizeC[0] = cc;
core.sizeT[0] = tc;
core.imageCount[0] = zc * tc * cc;
core.currentOrder[0] = "XYZCT";
if (stks.length > 1) {
CoreMetadata newCore = new CoreMetadata(stks.length);
for (int i=0; i<stks.length; i++) {
newCore.sizeX[i] = core.sizeX[0];
newCore.sizeY[i] = core.sizeY[0];
newCore.sizeZ[i] = core.sizeZ[0];
newCore.sizeC[i] = core.sizeC[0];
newCore.sizeT[i] = core.sizeT[0];
newCore.pixelType[i] = core.pixelType[0];
newCore.imageCount[i] = core.imageCount[0];
newCore.currentOrder[i] = core.currentOrder[0];
newCore.rgb[i] = core.rgb[0];
newCore.littleEndian[i] = core.littleEndian[0];
newCore.interleaved[i] = core.interleaved[0];
newCore.orderCertain[i] = true;
}
newCore.sizeC[0] = stks[0].length / newCore.sizeT[0];
newCore.sizeC[1] = stks[1].length / newCore.sizeT[1];
newCore.sizeZ[1] = 1;
newCore.imageCount[0] =
newCore.sizeC[0] * newCore.sizeT[0] * newCore.sizeZ[0];
newCore.imageCount[1] = newCore.sizeC[1] * newCore.sizeT[1];
core = newCore;
}
}
}
// -- Internal BaseTiffReader API methods --
/* @see BaseTiffReader#initStandardMetadata() */
protected void initStandardMetadata() throws FormatException, IOException {
super.initStandardMetadata();
try {
// Now that the base TIFF standard metadata has been parsed, we need to
// parse out the STK metadata from the UIC4TAG.
TiffIFDEntry uic1tagEntry = TiffTools.getFirstIFDEntry(in, UIC1TAG);
TiffIFDEntry uic2tagEntry = TiffTools.getFirstIFDEntry(in, UIC2TAG);
TiffIFDEntry uic4tagEntry = TiffTools.getFirstIFDEntry(in, UIC4TAG);
int planes = uic4tagEntry.getValueCount();
mmPlanes = planes;
parseUIC2Tags(uic2tagEntry.getValueOffset());
parseUIC4Tags(uic4tagEntry.getValueOffset());
parseUIC1Tags(uic1tagEntry.getValueOffset(),
uic1tagEntry.getValueCount());
in.seek(uic4tagEntry.getValueOffset());
// copy ifds into a new array of Hashtables that will accommodate the
// additional image planes
long[] uic2 = TiffTools.getIFDLongArray(ifds[0], UIC2TAG, true);
core.imageCount[0] = uic2.length;
long[] uic3 = TiffTools.getIFDLongArray(ifds[0], UIC3TAG, true);
for (int i=0; i<uic3.length; i++) {
in.seek(uic3[i]);
put("Wavelength [" + intFormatMax(i, mmPlanes) + "]",
in.readLong() / in.readLong());
}
Hashtable[] tempIFDs = new Hashtable[core.imageCount[0]];
long[] oldOffsets = TiffTools.getIFDLongArray(ifds[0],
TiffTools.STRIP_OFFSETS, true);
long[] stripByteCounts = TiffTools.getIFDLongArray(ifds[0],
TiffTools.STRIP_BYTE_COUNTS, true);
int stripsPerImage = oldOffsets.length;
int check = TiffTools.getIFDIntValue(ifds[0],
TiffTools.PHOTOMETRIC_INTERPRETATION);
if (check == TiffTools.RGB_PALETTE) {
TiffTools.putIFDValue(ifds[0], TiffTools.PHOTOMETRIC_INTERPRETATION,
TiffTools.BLACK_IS_ZERO);
}
emWavelength = TiffTools.getIFDLongArray(ifds[0], UIC3TAG, true);
// for each image plane, construct an IFD hashtable
int pointer = 0;
Hashtable temp;
for(int i=0; i<core.imageCount[0]; i++) {
temp = new Hashtable();
// copy most of the data from 1st IFD
temp.put(new Integer(TiffTools.LITTLE_ENDIAN), ifds[0].get(
new Integer(TiffTools.LITTLE_ENDIAN)));
temp.put(new Integer(TiffTools.IMAGE_WIDTH), ifds[0].get(
new Integer(TiffTools.IMAGE_WIDTH)));
temp.put(new Integer(TiffTools.IMAGE_LENGTH),
ifds[0].get(new Integer(TiffTools.IMAGE_LENGTH)));
temp.put(new Integer(TiffTools.BITS_PER_SAMPLE), ifds[0].get(
new Integer(TiffTools.BITS_PER_SAMPLE)));
temp.put(new Integer(TiffTools.COMPRESSION), ifds[0].get(
new Integer(TiffTools.COMPRESSION)));
temp.put(new Integer(TiffTools.PHOTOMETRIC_INTERPRETATION),
ifds[0].get(new Integer(TiffTools.PHOTOMETRIC_INTERPRETATION)));
temp.put(new Integer(TiffTools.STRIP_BYTE_COUNTS), ifds[0].get(
new Integer(TiffTools.STRIP_BYTE_COUNTS)));
temp.put(new Integer(TiffTools.ROWS_PER_STRIP), ifds[0].get(
new Integer(TiffTools.ROWS_PER_STRIP)));
temp.put(new Integer(TiffTools.X_RESOLUTION), ifds[0].get(
new Integer(TiffTools.X_RESOLUTION)));
temp.put(new Integer(TiffTools.Y_RESOLUTION), ifds[0].get(
new Integer(TiffTools.Y_RESOLUTION)));
temp.put(new Integer(TiffTools.RESOLUTION_UNIT), ifds[0].get(
new Integer(TiffTools.RESOLUTION_UNIT)));
temp.put(new Integer(TiffTools.PREDICTOR), ifds[0].get(
new Integer(TiffTools.PREDICTOR)));
// now we need a StripOffsets entry
long planeOffset = i*(oldOffsets[stripsPerImage - 1] +
stripByteCounts[stripsPerImage - 1] - oldOffsets[0]);
long[] newOffsets = new long[oldOffsets.length];
newOffsets[0] = planeOffset + oldOffsets[0];
for(int j=1; j<newOffsets.length; j++) {
newOffsets[j] = newOffsets[j-1] + stripByteCounts[0];
}
temp.put(new Integer(TiffTools.STRIP_OFFSETS), newOffsets);
tempIFDs[pointer] = temp;
pointer++;
}
ifds = tempIFDs;
}
catch (UnknownTagException exc) { trace(exc); }
catch (NullPointerException exc) { trace(exc); }
catch (IOException exc) { trace(exc); }
catch (FormatException exc) { trace(exc); }
try {
super.initStandardMetadata();
}
catch (FormatException exc) {
if (debug) trace(exc);
}
catch (IOException exc) {
if (debug) trace(exc);
}
// parse (mangle) TIFF comment
String descr = (String) getMeta("Comment");
if (descr != null) {
StringTokenizer st = new StringTokenizer(descr, "\n");
StringBuffer sb = new StringBuffer();
boolean first = true;
while (st.hasMoreTokens()) {
String line = st.nextToken();
int colon = line.indexOf(": ");
if (colon < 0) {
// normal line (not a key/value pair)
if (line.trim().length() > 0) {
// not a blank line
sb.append(line);
if (!line.endsWith(".")) sb.append(".");
sb.append(" ");
}
first = false;
continue;
}
if (first) {
// first line could be mangled; make a reasonable guess
int dot = line.lastIndexOf(".", colon);
if (dot >= 0) {
String s = line.substring(0, dot + 1);
sb.append(s);
if (!s.endsWith(".")) sb.append(".");
sb.append(" ");
}
line = line.substring(dot + 1);
colon -= dot + 1;
first = false;
}
// add key/value pair embedded in comment as separate metadata
String key = line.substring(0, colon);
String value = line.substring(colon + 2);
put(key, value);
}
// replace comment with trimmed version
descr = sb.toString().trim();
if (descr.equals("")) metadata.remove("Comment");
else put("Comment", descr);
}
try {
if (core.sizeZ[0] == 0) {
core.sizeZ[0] =
TiffTools.getIFDLongArray(ifds[0], UIC2TAG, true).length;
}
if (core.sizeT[0] == 0) core.sizeT[0] = getImageCount() / core.sizeZ[0];
}
catch (FormatException exc) {
if (debug) trace(exc);
}
}
/* @see BaseTiffReader#getImageName() */
protected String getImageName() {
if (imageName == null) return super.getImageName();
return imageName;
}
/* @see BaseTiffReader#getImageCreationDate() */
protected String getImageCreationDate() {
if (imageCreationDate == null) return super.getImageCreationDate();
return imageCreationDate;
}
Integer getEmWave(int i) {
if (emWavelength == null || emWavelength[i] == 0) return null;
return new Integer((int) emWavelength[i]);
}
// -- Helper methods --
/**
* Populates metadata fields with some contained in MetaMorph UIC2 Tag.
* (for each plane: 6 integers:
* zdistance numerator, zdistance denominator,
* creation date, creation time, modif date, modif time)
* @param uic2offset offset to UIC2 (33629) tag entries
*
* not a regular tiff tag (6*N entries, N being the tagCount)
* @throws IOException
*/
void parseUIC2Tags(long uic2offset) throws IOException {
long saveLoc = in.getFilePointer();
in.seek(uic2offset);
/*number of days since the 1st of January 4713 B.C*/
int cDate;
/*milliseconds since 0:00*/
int cTime;
/*z step, distance separating previous slice from current one*/
double zDistance;
String iAsString;
for (int i=0; i<mmPlanes; i++) {
iAsString = intFormatMax(i, mmPlanes);
int num = in.readInt();
int den = in.readInt();
zDistance = (double) num / den;
put("zDistance[" + iAsString + "]", zDistance);
cDate = in.readInt();
put("creationDate[" + iAsString + "]", decodeDate(cDate));
cTime = in.readInt();
put("creationTime[" + iAsString + "]", decodeTime(cTime));
// modification date and time are skipped as they all seem equal to 0...?
in.skip(8);
}
in.seek(saveLoc);
}
/**
* UIC4 metadata parser
*
* UIC4 Table contains per-plane blocks of metadata
* stage X/Y positions,
* camera chip offsets,
* stage labels...
* @param long uic4offset: offset of UIC4 table (not tiff-compliant)
* @throws IOException
*/
private void parseUIC4Tags(long uic4offset) throws IOException {
long saveLoc = in.getFilePointer();
in.seek(uic4offset);
boolean end=false;
short id;
while (!end) {
id = in.readShort();
switch (id) {
case 0:
end=true;
break;
case 28:
readStagePositions();
break;
case 29:
readCameraChipOffsets();
break;
case 37:
readStageLabels();
break;
case 40:
readAbsoluteZ();
break;
case 41:
readAbsoluteZValid();
break;
default:
//unknown tags: do nothing
break;
//28->stagePositions
//29->cameraChipOffsets
//30->stageLabel
//40->AbsoluteZ
//41AbsoluteZValid
//0->end
}
}
in.seek(saveLoc);
}
void readStagePositions() throws IOException {
int nx, dx, ny, dy;
// for each plane:
// 2 ints (rational:numerator,denominator) for stage X,
// 2 ints (idem) for stage Y position
double xPosition, yPosition;
String iAsString;
for(int i=0; i<mmPlanes; i++) {
nx = in.readInt();
dx = in.readInt();
ny = in.readInt();
dy = in.readInt();
xPosition = (dx == 0) ? Double.NaN : (double) nx / dx;
yPosition = (dy == 0) ? Double.NaN : (double) ny / dy;
iAsString = intFormatMax(i, mmPlanes);
put("stageX[" + iAsString + "]", xPosition);
put("stageY[" + iAsString + "]", yPosition);
}
}
void readCameraChipOffsets() throws IOException {
int nx, dx, ny, dy;
double cameraXChipOffset, cameraYChipOffset;
String iAsString;
for(int i=0; i<mmPlanes; i++) {
iAsString = intFormatMax(i, mmPlanes);
nx = in.readInt();
dx = in.readInt();
ny = in.readInt();
dy = in.readInt();
cameraXChipOffset = (dx == 0) ? Double.NaN: (double) nx / dx;
cameraYChipOffset = (dy == 0) ? Double.NaN: (double) ny/ dy;
put("cameraXChipOffset[" + iAsString + "]", cameraXChipOffset);
put("cameraYChipOffset[" + iAsString + "]", cameraYChipOffset);
}
}
void readStageLabels() throws IOException {
int strlen;
byte[] curlabel;
String iAsString;
for (int i=0; i<mmPlanes; i++) {
iAsString = intFormatMax(i, mmPlanes);
strlen = in.readInt();
curlabel = new byte[strlen];
in.read(curlabel);
put("stageLabel[" + iAsString + "]", new String(curlabel));
}
}
void readAbsoluteZ() throws IOException {
int nz, dz;
double absoluteZ;
for(int i=0; i<mmPlanes; i++) {
nz = in.readInt();
dz = in.readInt();
absoluteZ = (dz == 0) ? Double.NaN : (double) nz / dz;
put("absoluteZ[" + intFormatMax(i, mmPlanes) + "]", absoluteZ);
}
}
void readAbsoluteZValid() throws IOException {
for (int i=0; i<mmPlanes; i++) {
put("absoluteZValid[" + intFormatMax(i, mmPlanes) + "]", in.readInt());
}
}
/**
* UIC1 entry parser
* @throws IOException
* @param long uic1offset : offset as found in the tiff tag 33628 (UIC1Tag)
* @param int uic1count : number of entries in UIC1 table (not tiff-compliant)
*/
private void parseUIC1Tags(long uic1offset, int uic1count) throws IOException
{
// Loop through and parse out each field. A field whose
// code is "0" represents the end of the fields so we'll stop
// when we reach that; much like a NULL terminated C string.
long saveLoc = in.getFilePointer();
in.seek(uic1offset);
int currentID, valOrOffset;
// variable declarations, because switch is dumb
int num, denom;
String thedate, thetime;
long lastOffset;
byte[] toread;
for (int i=0; i<uic1count; i++) {
currentID = in.readInt();
valOrOffset = in.readInt();
switch (currentID) {
case 1:
put("MinScale", valOrOffset);
break;
case 2:
put("MaxScale", valOrOffset);
break;
case 3:
int calib = valOrOffset;
String calibration = calib != 0 ? "on" : "off";
put("Spatial Calibration", calibration);
break;
case 4:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
num = in.readInt();
denom = in.readInt();
put("XCalibration", new TiffRational(num, denom));
in.seek(lastOffset);
break;
case 5:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
num = in.readInt();
denom = in.readInt();
put("YCalibration", new TiffRational(num, denom));
in.seek(lastOffset);
break;
case 6:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
num = in.readInt();
toread = new byte[num];
in.read(toread);
put("CalibrationUnits", new String(toread));
in.seek(lastOffset);
break;
case 7:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
num = in.readInt();
toread = new byte[num];
in.read(toread);
String name = new String(toread);
put("Name", name);
imageName = name;
in.seek(lastOffset);
break;
case 8:
int thresh = valOrOffset;
String threshState = "off";
if (thresh == 1) threshState = "inside";
else if (thresh == 2) threshState = "outside";
put("ThreshState", threshState);
break;
case 9:
put("ThreshStateRed", valOrOffset);
break;
// there is no 10
case 11:
put("ThreshStateGreen", valOrOffset);
break;
case 12:
put("ThreshStateBlue", valOrOffset);
break;
case 13:
put("ThreshStateLo", valOrOffset);
break;
case 14:
put("ThreshStateHi", valOrOffset);
break;
case 15:
int zoom = valOrOffset;
put("Zoom", zoom);
//OMETools.setAttribute(ome, "DisplayOptions", "Zoom", "" + zoom);
break;
case 16: // oh how we hate you Julian format...
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
thedate = decodeDate(in.readInt());
thetime = decodeTime(in.readInt());
put("DateTime", thedate + " " + thetime);
imageCreationDate = thedate + " " + thetime;
in.seek(lastOffset);
break;
case 17:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
thedate = decodeDate(in.readInt());
thetime = decodeTime(in.readInt());
put("LastSavedTime", thedate + " " + thetime);
in.seek(lastOffset);
break;
case 18:
put("currentBuffer", valOrOffset);
break;
case 19:
put("grayFit", valOrOffset);
break;
case 20:
put("grayPointCount", valOrOffset);
break;
case 21:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
num = in.readInt();
denom = in.readInt();
put("grayX", new TiffRational(num, denom));
in.seek(lastOffset);
break;
case 22:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
num = in.readInt();
denom = in.readInt();
put("gray", new TiffRational(num, denom));
in.seek(lastOffset);
break;
case 23:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
num = in.readInt();
denom = in.readInt();
put("grayMin", new TiffRational(num, denom));
in.seek(lastOffset);
break;
case 24:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
num = in.readInt();
denom = in.readInt();
put("grayMax", new TiffRational(num, denom));
in.seek(lastOffset);
break;
case 25:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
num = in.readInt();
toread = new byte[num];
in.read(toread);
put("grayUnitName", new String(toread));
in.seek(lastOffset);
break;
case 26:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
int standardLUT = in.readInt();
in.seek(lastOffset);
String standLUT;
switch (standardLUT) {
case 0:
standLUT = "monochrome";
break;
case 1:
standLUT = "pseudocolor";
break;
case 2:
standLUT = "Red";
break;
case 3:
standLUT = "Green";
break;
case 4:
standLUT = "Blue";
break;
case 5:
standLUT = "user-defined";
break;
default:
standLUT = "monochrome"; break;
}
put("StandardLUT", standLUT);
break;
case 27:
put("Wavelength", valOrOffset);
break;
case 30:
put("OverlayMask", valOrOffset);
break;
case 31:
put("OverlayCompress", valOrOffset);
break;
case 32:
put("Overlay", valOrOffset);
break;
case 33:
put("SpecialOverlayMask", valOrOffset);
break;
case 34:
put("SpecialOverlayCompress", in.readInt());
break;
case 35:
put("SpecialOverlay", valOrOffset);
break;
case 36:
put("ImageProperty", valOrOffset);
break;
case 38:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
num = in.readInt();
denom = in.readInt();
put("AutoScaleLoInfo", new TiffRational(num, denom));
in.seek(lastOffset);
break;
case 39:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
num = in.readInt();
denom = in.readInt();
put("AutoScaleHiInfo", new TiffRational(num, denom));
in.seek(lastOffset);
break;
case 42:
put("Gamma", valOrOffset);
break;
case 43:
put("GammaRed", valOrOffset);
break;
case 44:
put("GammaGreen", valOrOffset);
break;
case 45:
put("GammaBlue", valOrOffset);
break;
case 46:
lastOffset = in.getFilePointer();
in.seek(valOrOffset);
int xBin, yBin;
xBin = in.readInt();
yBin = in.readInt();
put("CameraBin", new String("(" + xBin + "," + yBin + ")"));
in.seek(lastOffset);
break;
default:
break;
}
}
in.seek(saveLoc);
}
// -- Utility methods --
/** Converts a Julian date value into a human-readable string. */
public static String decodeDate(int julian) {
long a, b, c, d, e, alpha, z;
short day, month, year;
// code reused from the Metamorph data specification
z = julian + 1;
if (z < 2299161L) a = z;
else {
alpha = (long) ((z - 1867216.25) / 36524.25);
a = z + 1 + alpha - alpha / 4;
}
b = (a > 1721423L ? a + 1524 : a + 1158);
c = (long) ((b - 122.1) / 365.25);
d = (long) (365.25 * c);
e = (long) ((b - d) / 30.6001);
day = (short) (b - d - (long) (30.6001 * e));
month = (short) ((e < 13.5) ? e - 1 : e - 13);
year = (short) ((month > 2.5) ? (c - 4716) : c - 4715);
return day + "/" + month + "/" + year;
}
/** Converts a time value in milliseconds into a human-readable string. */
public static String decodeTime(int millis) {
int ms, seconds, minutes, hours;
ms = millis % 1000;
millis -= ms;
millis /= 1000;
seconds = millis % 60;
millis -= seconds;
millis /= 60;
minutes = millis % 60;
millis -= minutes;
millis /= 60;
hours = millis;
return intFormat(hours, 2) + ":" + intFormat(minutes, 2) + ":" +
intFormat(seconds, 2) + "." + intFormat(ms, 3);
}
/** Formats an integer value with leading 0s if needed. */
public static String intFormat(int myint, int digits) {
String formatstring = "0";
while (formatstring.length() < digits) {
formatstring += "0";
}
DecimalFormat df = new DecimalFormat(formatstring);
return df.format(myint);
}
/**
* Formats an integer with leading 0 using maximum sequence number.
*
* @param myint integer to format
* @param maxint max of "myint"
* @return String
*/
public static String intFormatMax(int myint, int maxint) {
return intFormat(myint, new Integer(maxint).toString().length());
}
}