/* * #%L * OME Bio-Formats package for reading and converting biological file formats. * %% * Copyright (C) 2005 - 2015 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-2.0.html>. * #L% */ package loci.formats.in; import java.io.EOFException; import java.io.IOException; import java.util.Arrays; import java.util.ArrayList; import java.util.Hashtable; import java.util.Vector; import loci.common.Constants; import loci.common.DataTools; import loci.common.RandomAccessInputStream; import loci.formats.CoreMetadata; import loci.formats.FormatException; import loci.formats.FormatReader; import loci.formats.FormatTools; import loci.formats.MetadataTools; import loci.formats.meta.MetadataStore; import loci.formats.MissingLibraryException; import ome.xml.model.primitives.PositiveFloat; import ome.units.quantity.Length; import ome.units.quantity.Time; import ome.units.UNITS; import org.scijava.nativelib.NativeLibraryUtil; /** * SlideBook6Reader is a file format reader for 3i SlideBook SLD files that uses * the SlideBook SBReadFile SDK via the (Windows only) dlls: * SBReadFile.dll and SlideBook6Reader.dll * * @author Richard Myers, richard at intelligent-imaging.com * */ public class SlideBook6Reader extends FormatReader { // -- Constants -- public static final int SLD_MAGIC_BYTES_1_0 = 0x006c; public static final int SLD_MAGIC_BYTES_1_1 = 0x0100; public static final int SLD_MAGIC_BYTES_1_2 = 0x0200; public static final int SLD_MAGIC_BYTES_2_0 = 0x01f5; public static final int SLD_MAGIC_BYTES_2_1 = 0x0102; public static final long SLD_MAGIC_BYTES_3 = 0xf6010101L; private static final String URL_3I_SLD = "http://www.openmicroscopy.org/site/support/bio-formats/formats/3i-slidebook6.html"; private static final String NO_3I_MSG = "3i SlideBook SlideBook6Reader library not found. " + "Please see " + URL_3I_SLD + " for details."; private static final String GENERAL_3I_MSG = "3i SlideBook SlideBook6Reader library problem. " + "Please see " + URL_3I_SLD + " for details."; // -- Static initializers -- private static boolean libraryFound = false; static { try { // load JNI wrapper of SBReadFile.dll NativeLibraryUtil.Architecture arch = NativeLibraryUtil.getArchitecture(); if (arch != NativeLibraryUtil.Architecture.WINDOWS_64 && arch != NativeLibraryUtil.Architecture.WINDOWS_32) { // TODO: add compiled linux and OS X architecture libraries to class throw new UnsatisfiedLinkError(); } if (!libraryFound) { libraryFound = NativeLibraryUtil.loadNativeLibrary(SlideBook6Reader.class, "SlideBook6Reader"); } } catch (UnsatisfiedLinkError e) { // log level debug, otherwise a warning will be printed every time a file is initialized without the .dll present LOGGER.debug(NO_3I_MSG, e); libraryFound = false; } catch (SecurityException e) { LOGGER.warn("Insufficient permission to load native library", e); libraryFound = false; } } // -- Constructor -- public SlideBook6Reader() { super("SlideBook 6 SLD (native)", new String[] {"sld"}); domains = new String[] {FormatTools.LM_DOMAIN}; suffixSufficient = false; } // -- IFormatReader API methods -- /* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */ public boolean isThisType(RandomAccessInputStream stream) throws IOException { final int blockLen = 8; stream.seek(4); boolean littleEndian = stream.readString(2).equals("II"); if (!FormatTools.validStream(stream, blockLen, littleEndian)) return false; int magicBytes1 = stream.readShort(); int magicBytes2 = stream.readShort(); boolean isMatch = ((magicBytes2 & 0xff00) == SLD_MAGIC_BYTES_1_1 || (magicBytes2 & 0xff00) == SLD_MAGIC_BYTES_1_2) && (magicBytes1 == SLD_MAGIC_BYTES_1_0 || magicBytes1 == SLD_MAGIC_BYTES_2_0); return isMatch; } /* @see loci.formats.IFormatReader#isThisType(String, boolean) */ public boolean isThisType(String file, boolean open) { // Check the first few bytes to determine if the file can be read by this reader. return libraryFound && super.isThisType(file, open); } /** * @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int) */ public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) throws FormatException, IOException { FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h); int[] zct = FormatTools.getZCTCoords(this, no); int bpc = FormatTools.getBytesPerPixel(getPixelType()); byte[] b = new byte[FormatTools.getPlaneSize(this)]; readImagePlaneBuf(b, getSeries(), 0, zct[2], zct[0], zct[1]); int pixel = bpc * getRGBChannelCount(); int rowLen = w * pixel; for (int row=0; row<h; row++) { System.arraycopy(b, pixel * ((row + y) * getSizeX() + x), buf, row * rowLen, rowLen); } if (isRGB()) { int bpp = getSizeC() * bpc; int line = w * bpp; for (int row=0; row<h; row++) { for (int col=0; col<w; col++) { int base = row * line + col * bpp; for (int bb=0; bb<bpc; bb++) { byte blue = buf[base + bpc*(getSizeC() - 1) + bb]; buf[base + bpc*(getSizeC() - 1) + bb] = buf[base + bb]; buf[base + bb] = blue; } } } } return buf; } // -- Internal FormatReader API methods -- public void close(boolean fileOnly) throws IOException { super.close(fileOnly); if (libraryFound) { closeFile(); } } /* @see loci.formats.FormatReader#initFile(String) */ protected void initFile(String id) throws FormatException, IOException { super.initFile(id); try { openFile(id); // read basic meta data int numCaptures = getNumCaptures(); int[] numPositions = new int[numCaptures]; int[] numTimepoints = new int[numCaptures]; int[] numZPlanes = new int[numCaptures]; int[] numChannels = new int[numCaptures]; for (int capture=0; capture < numCaptures; capture++) { numPositions[capture] = getNumPositions(capture); numTimepoints[capture] = getNumTimepoints(capture) / numPositions[capture]; numZPlanes[capture] = getNumZPlanes(capture); numChannels[capture] = getNumChannels(capture); } core.clear(); // set up basic meta data for (int capture=0; capture < numCaptures; capture++) { CoreMetadata ms = new CoreMetadata(); core.add(ms); setSeries(capture); ms.sizeX = getNumXColumns(capture); if (ms.sizeX % 2 != 0) ms.sizeX++; ms.sizeY = getNumYRows(capture); ms.sizeZ = numZPlanes[capture]; ms.sizeT = numTimepoints[capture] * numPositions[capture]; ms.sizeC = numChannels[capture]; int bytes = getBytesPerPixel(capture); if (bytes % 3 == 0) { ms.sizeC *= 3; bytes /= 3; ms.rgb = true; } else ms.rgb = false; ms.pixelType = FormatTools.pixelTypeFromBytes(bytes, false, true); ms.imageCount = ms.sizeZ * ms.sizeT; if (!ms.rgb) ms.imageCount *= ms.sizeC; ms.interleaved = true; ms.littleEndian = true; ms.dimensionOrder = "XYCZT"; ms.indexed = false; ms.falseColor = false; } setSeries(0); // fill in meta data MetadataStore store = makeFilterMetadata(); MetadataTools.populatePixels(store, this, true); // add extended meta data if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) { // set instrument information String instrumentID = MetadataTools.createLSID("Instrument", 0); store.setInstrumentID(instrumentID, 0); // set up extended meta data for (int capture=0; capture < numCaptures; capture++) { // link Instrument and Image store.setImageInstrumentRef(instrumentID, capture); // set image name String imageName = getImageName(capture); store.setImageName(imageName, capture); // store.setImageName("Image " + (capture + 1), capture); // set description String imageDescription = getImageComments(capture); store.setImageDescription(imageDescription, capture); // set voxel size per image (microns) double voxelsize = getVoxelSize(capture); Length physicalSizeX = FormatTools.getPhysicalSizeX(voxelsize); Length physicalSizeY = FormatTools.getPhysicalSizeY(voxelsize); if (physicalSizeX != null) { store.setPixelsPhysicalSizeX(physicalSizeX, capture); } if (physicalSizeY != null) { store.setPixelsPhysicalSizeY(physicalSizeY, capture); } double stepSize = 0; if (numZPlanes[capture] > 1) { double plane0 = getZPosition(capture, 0, 0); double plane1 = getZPosition(capture, 0, getNumChannels(capture)); // distance between plane 0 and 1 is step size, assume constant for all planes stepSize = plane1 - plane0; } Length physicalSizeZ = FormatTools.getPhysicalSizeZ(stepSize); if (physicalSizeZ != null) { store.setPixelsPhysicalSizeZ(physicalSizeZ, capture); } int imageIndex = 0; // if numPositions[capture] > 1 then we have a montage for (int timepoint = 0; timepoint < numTimepoints[capture]; timepoint++) { int deltaT = getElapsedTime(capture, timepoint); for (int position = 0; position < numPositions[capture]; position++) { for (int zplane = 0; zplane < numZPlanes[capture]; zplane++) { for (int channel = 0; channel < numChannels[capture]; channel++, imageIndex++) { // set elapsed time store.setPlaneDeltaT(new Time(deltaT, UNITS.MS), capture, imageIndex); // set exposure time int expTime = getExposureTime(capture, channel); store.setPlaneExposureTime(new Time(expTime, UNITS.MS), capture, imageIndex); // set tile xy position double numberX = getXPosition(capture, position); Length positionX = new Length(numberX, UNITS.MICROM); store.setPlanePositionX(positionX, capture, imageIndex); double numberY = getYPosition(capture, position); Length positionY = new Length(numberY, UNITS.MICROM); store.setPlanePositionY(positionY, capture, imageIndex); // set tile z position double positionZ = getZPosition(capture, position, zplane); Length zPos = new Length(positionZ, UNITS.MICROM); store.setPlanePositionZ(zPos, capture, imageIndex); } } } } // set channel names for (int channel = 0; channel < numChannels[capture]; channel++) { String theChannelName = getChannelName(capture, channel); store.setChannelName(theChannelName.trim(), capture, channel); } } // populate Objective data int objectiveIndex = 0; for (int capture = 0; capture < numCaptures; capture++) { // link Objective to Image String objectiveID = MetadataTools.createLSID("Objective", 0, objectiveIndex); store.setObjectiveID(objectiveID, 0, objectiveIndex); store.setObjectiveSettingsID(objectiveID, capture); String objective = getLensName(capture); if (objective != null) { store.setObjectiveModel(objective, 0, objectiveIndex); } store.setObjectiveCorrection(getCorrection("Other"), 0, objectiveIndex); store.setObjectiveImmersion(getImmersion("Other"), 0, objectiveIndex); double magnification = getMagnification(capture); if (magnification > 0) { store.setObjectiveNominalMagnification(magnification, 0, objectiveIndex); } objectiveIndex++; } } } catch (UnsatisfiedLinkError e) { throw new MissingLibraryException(GENERAL_3I_MSG, e); } catch (Exception e) { throw new MissingLibraryException(GENERAL_3I_MSG, e); } } // -- Native methods -- public native boolean openFile(String path); public native void closeFile(); public native int getNumCaptures(); public native int getNumPositions(int inCapture); public native int getNumTimepoints(int inCapture); public native int getNumChannels(int inCapture); public native int getNumXColumns(int inCapture); public native int getNumYRows(int inCapture); public native int getNumZPlanes(int inCapture); public native int getElapsedTime(int inCapture, int inTimepoint); public native int getExposureTime(int inCapture, int inChannel); public native float getVoxelSize(int inCapture); public native double getXPosition(int inCapture, int inPosition); public native double getYPosition(int inCapture, int inPosition); public native double getZPosition(int inCapture, int inPosition, int inZPlane); public native int getMontageRow(int inCapture, int inPosition); public native int getMontageColumn(int inCapture, int inPosition); public native String getChannelName(int inCapture, int inChannel); public native String getLensName(int inCapture); public native double getMagnification(int inCapture); public native String getImageName(int inCapture); public native String getImageComments(int inCapture); public native int getBytesPerPixel(int inCapture); public native boolean readImagePlaneBuf( byte outPlaneBuffer[], int inCapture, int inPosition, int inTimepoint, int inZ, int inChannel ); }