// // GatanReader.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.IOException; import java.util.Vector; import loci.formats.*; /** * GatanReader is the file format reader for Gatan files. * * <dl><dt><b>Source code:</b></dt> * <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/loci/formats/in/GatanReader.java">Trac</a>, * <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/loci/formats/in/GatanReader.java">SVN</a></dd></dl> * * @author Melissa Linkert linkert at wisc.edu */ public class GatanReader extends FormatReader { // -- Fields -- /** Offset to pixel data. */ private long pixelOffset; /** List of pixel sizes. */ private Vector pixelSizes; private int bytesPerPixel; protected int pixelDataNum = 0; protected int datatype; // -- Constructor -- /** Constructs a new Gatan reader. */ public GatanReader() { super("Gatan Digital Micrograph", "dm3"); } // -- IFormatReader API methods -- /* @see loci.formats.IFormatReader#isThisType(byte[]) */ public boolean isThisType(byte[] block) { if (block == null || block.length < 4) return false; return DataTools.bytesToInt(block, false) == 3; } /* @see loci.formats.IFormatReader#openBytes(int, byte[]) */ public byte[] openBytes(int no, byte[] buf) throws FormatException, IOException { FormatTools.assertId(currentId, true, 1); FormatTools.checkPlaneNumber(this, no); FormatTools.checkBufferSize(this, buf.length); in.seek(pixelOffset); in.read(buf); return buf; } // -- Internal FormatReader API methods -- /* @see loci.formats.FormatReader#initFile(String) */ protected void initFile(String id) throws FormatException, IOException { if (debug) debug("GatanReader.initFile(" + id + ")"); super.initFile(id); in = new RandomAccessStream(id); pixelOffset = 0; status("Verifying Gatan format"); core.littleEndian[0] = false; pixelSizes = new Vector(); in.order(false); // only support version 3 if (in.readInt() != 3) { throw new FormatException("invalid header"); } status("Reading tags"); in.skipBytes(4); core.littleEndian[0] = in.readInt() == 1; // TagGroup instance in.skipBytes(2); in.order(!core.littleEndian[0]); parseTags(in.readInt(), "initFile"); status("Populating metadata"); switch (datatype) { case 1: case 10: core.pixelType[0] = FormatTools.UINT16; break; case 2: case 3: case 5: case 12: case 13: core.pixelType[0] = FormatTools.FLOAT; break; case 7: case 8: case 11: case 23: core.pixelType[0] = FormatTools.UINT32; break; default: core.pixelType[0] = FormatTools.UINT8; } core.sizeZ[0] = 1; core.sizeC[0] = 1; core.sizeT[0] = 1; core.currentOrder[0] = "XYZTC"; core.imageCount[0] = 1; core.rgb[0] = false; core.interleaved[0] = false; core.metadataComplete[0] = true; core.indexed[0] = false; core.falseColor[0] = false; // The metadata store we're working with. MetadataStore store = getMetadataStore(); store.setImage(currentId, null, null, null); FormatTools.populatePixels(store, this); Float pixX = new Float(1); Float pixY = new Float(1); Float pixZ = new Float(1); if (pixelSizes.size() > 0) { pixX = new Float((String) pixelSizes.get(0)); } if (pixelSizes.size() > 1) { pixY = new Float((String) pixelSizes.get(1)); } if (pixelSizes.size() > 2) { pixZ = new Float((String) pixelSizes.get(2)); } store.setDimensions(pixX, pixY, pixZ, null, null, null); String gamma = (String) getMeta("Gamma"); for (int i=0; i<core.sizeC[0]; i++) { store.setLogicalChannel(i, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); store.setDisplayChannel(new Integer(i), null, null, gamma == null ? null : new Float(gamma), null); } String mag = (String) getMeta("Indicated Magnification"); store.setObjective(null, null, null, null, mag == null ? null : new Float(mag), null, null); } // -- Helper methods -- /** * Parses Gatan DM3 tags. * Information on the DM3 structure found at: * http://rsb.info.nih.gov/ij/plugins/DM3Format.gj.html and * http://www-hrem.msm.cam.ac.uk/~cbb/info/dmformat/ */ private void parseTags(int numTags, String parent) throws IOException { for (int i=0; i<numTags; i++) { byte type = in.readByte(); // can be 21 (data) or 20 (tag group) int length = in.readShort(); String labelString = in.readString(length); // image data is in tag with type 21 and label 'Data' // image dimensions are in type 20 tag with 2 type 15 tags // bytes per pixel is in type 21 tag with label 'PixelDepth' if (type == 21) { in.skipBytes(4); // equal to '%%%%' int n = in.readInt(); int dataType = 0; if (n == 1) { dataType = in.readInt(); String data; in.order(core.littleEndian[0]); switch (dataType) { case 2: case 4: data = "" + in.readShort(); break; case 3: case 5: data = "" + in.readInt(); break; case 6: data = "" + in.readFloat(); break; case 7: data = "" + in.readFloat(); in.skipBytes(4); break; case 8: case 9: case 10: data = "" + in.read(); break; default: data = "0"; } if (parent.equals("Dimensions")) { if (i == 0) core.sizeX[0] = Integer.parseInt(data); else if (i == 1) core.sizeY[0] = Integer.parseInt(data); } if (labelString.equals("PixelDepth")) { bytesPerPixel = Integer.parseInt(data); } else if (labelString.equals("Scale") && !data.equals("1.0") && !data.equals("0.0")) { pixelSizes.add(data); } addMeta(labelString, data); if (labelString.equals("DataType")) datatype = Integer.parseInt(data); in.order(!core.littleEndian[0]); } else if (n == 2) { in.order(core.littleEndian[0]); dataType = in.readInt(); if (dataType == 18) { // this should always be true length = in.readInt(); } addMeta(labelString, in.readString(length)); in.order(!core.littleEndian[0]); } else if (n == 3) { dataType = in.readInt(); if (dataType == 20) { // this should always be true dataType = in.readInt(); length = in.readInt(); if ("Data".equals(labelString)) pixelDataNum++; if ("Data".equals(labelString) /*&& pixelDataNum == 2*/) { // we're given the number of pixels, but the tag containing // bytes per pixel doesn't occur until after the image data // // this is a messy way to read pixel data, which uses the fact // that the first byte after the pixel data is either 20 or 21 byte check = 0; double bpp = 0.5; long pos = in.getFilePointer(); while (check != 20 && check != 21) { bpp *= 2; in.seek(pos); pixelOffset = pos; in.skipBytes((int) (bpp * length)); check = in.readByte(); } in.seek((long) (pos + bpp * length)); } else { int[] data = new int[length]; for (int j=0; j<length; j++) { if (dataType == 2 || dataType == 4) { data[j] = in.readShort(); } else if (dataType == 7) in.skipBytes(8); else if (dataType == 8 || dataType == 9) in.skipBytes(1); else { data[j] = in.readInt(); } } } } } else { dataType = in.readInt(); // this is a normal struct of simple types if (dataType == 15) { int skip = in.readInt(); int numFields = in.readInt(); for (int j=0; j<numFields; j++) { skip += in.readInt(); dataType = in.readInt(); switch (dataType) { case 2: case 4: skip += 2; break; case 3: case 5: case 6: skip += 4; break; case 7: skip += 8; break; case 8: case 9: skip += 1; break; } } in.skipBytes(skip); } else if (dataType == 20) { // this is an array of structs int skip = 0; dataType = in.readInt(); if (dataType == 15) { // should always be true skip += in.readInt(); int numFields = in.readInt(); for (int j=0; j<numFields; j++) { skip += in.readInt(); dataType = in.readInt(); switch (dataType) { case 2: case 4: skip += 2; break; case 3: case 5: case 6: skip += 4; break; case 7: skip += 8; break; case 8: case 9: skip += 1; break; } } } skip *= in.readInt(); in.skipBytes(skip); } } } else if (type == 20) { in.skipBytes(2); parseTags(in.readInt(), labelString); } } } }