// // ICSWriter.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.out; import java.io.ByteArrayOutputStream; import java.io.IOException; import loci.common.RandomAccessInputStream; import loci.common.RandomAccessOutputStream; import loci.formats.FormatException; import loci.formats.FormatTools; import loci.formats.FormatWriter; import loci.formats.MetadataTools; import loci.formats.meta.MetadataRetrieve; /** * ICSWriter is the file format writer for ICS files. It writes ICS version 1 * and 2 files. * * <dl><dt><b>Source code:</b></dt> * <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/bio-formats/src/loci/formats/out/ICSWriter.java">Trac</a>, * <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/out/ICSWriter.java;hb=HEAD">Gitweb</a></dd></dl> */ public class ICSWriter extends FormatWriter { // -- Fields -- private long dimensionOffset; private int dimensionLength; private long pixelOffset; private int lastPlane = -1; private RandomAccessOutputStream pixels; // -- Constructor -- public ICSWriter() { super("Image Cytometry Standard", new String[] {"ids", "ics"}); } // -- IFormatWriter API methods -- /** * @see loci.formats.IFormatWriter#saveBytes(int, byte[], int, int, int, int) */ public void saveBytes(int no, byte[] buf, int x, int y, int w, int h) throws FormatException, IOException { checkParams(no, buf, x, y, w, h); MetadataRetrieve meta = getMetadataRetrieve(); int sizeX = meta.getPixelsSizeX(series).getValue().intValue(); int sizeY = meta.getPixelsSizeY(series).getValue().intValue(); int pixelType = FormatTools.pixelTypeFromString(meta.getPixelsType(series).toString()); int bytesPerPixel = FormatTools.getBytesPerPixel(pixelType); int rgbChannels = getSamplesPerPixel(); int planeSize = sizeX * sizeY * rgbChannels * bytesPerPixel; if (!initialized[series][no]) { initialized[series][no] = true; if (!isFullPlane(x, y, w, h)) { // write a dummy plane that will be overwritten in sections pixels.seek(pixelOffset + (no + 1) * planeSize); } } pixels.seek(pixelOffset + no * planeSize); if (isFullPlane(x, y, w, h) && (interleaved || rgbChannels == 1)) { pixels.write(buf); } else { pixels.skipBytes(bytesPerPixel * rgbChannels * sizeX * y); for (int row=0; row<h; row++) { ByteArrayOutputStream strip = new ByteArrayOutputStream(); for (int col=0; col<w; col++) { for (int c=0; c<rgbChannels; c++) { int index = interleaved ? rgbChannels * (row * w + col) + c : w * (c * h + row) + col; strip.write(buf, index * bytesPerPixel, bytesPerPixel); } } pixels.skipBytes(bytesPerPixel * rgbChannels * x); pixels.write(strip.toByteArray()); pixels.skipBytes(bytesPerPixel * rgbChannels * (sizeX - w - x)); } } lastPlane = no; } /* @see loci.formats.IFormatWriter#canDoStacks() */ public boolean canDoStacks() { return true; } /* @see loci.formats.IFormatWriter#getPixelTypes(String) */ public int[] getPixelTypes(String codec) { return new int[] {FormatTools.INT8, FormatTools.UINT8, FormatTools.INT16, FormatTools.UINT16, FormatTools.INT32, FormatTools.UINT32, FormatTools.FLOAT}; } // -- IFormatHandler API methods -- /* @see loci.formats.IFormatHandler#setId(String) */ public void setId(String id) throws FormatException, IOException { super.setId(id); if (checkSuffix(currentId, "ids")) { String metadataFile = currentId.substring(0, currentId.lastIndexOf(".")); metadataFile += ".ics"; out = new RandomAccessOutputStream(metadataFile); } if (out.length() == 0) { out.writeBytes("\t\n"); if (checkSuffix(id, "ids")) { out.writeBytes("ics_version\t1.0\n"); } else { out.writeBytes("ics_version\t2.0\n"); } out.writeBytes("filename\t" + currentId + "\n"); out.writeBytes("layout\tparameters\t6\n"); MetadataRetrieve meta = getMetadataRetrieve(); MetadataTools.verifyMinimumPopulated(meta, series); int pixelType = FormatTools.pixelTypeFromString(meta.getPixelsType(series).toString()); dimensionOffset = out.getFilePointer(); int[] sizes = overwriteDimensions(meta); dimensionLength = (int) (out.getFilePointer() - dimensionOffset); if (validBits != 0) { out.writeBytes("layout\tsignificant_bits\t" + validBits + "\n"); } boolean signed = FormatTools.isSigned(pixelType); boolean littleEndian = !meta.getPixelsBinDataBigEndian(series, 0).booleanValue(); out.writeBytes("representation\tformat\t" + (pixelType == FormatTools.FLOAT ? "real\n" : "integer\n")); out.writeBytes("representation\tsign\t" + (signed ? "signed\n" : "unsigned\n")); out.writeBytes("representation\tcompression\tuncompressed\n"); out.writeBytes("representation\tbyte_order\t"); for (int i=0; i<sizes[0]/8; i++) { if (littleEndian) { out.writeBytes((i + 1) + "\t"); } else { out.writeBytes(((sizes[0] / 8) - i) + "\t"); } } out.writeBytes("\nparameter\tscale\t1.000000\t"); String order = meta.getPixelsDimensionOrder(series).getValue(); StringBuffer units = new StringBuffer(); for (int i=0; i<order.length(); i++) { char dim = order.charAt(i); Number value = 1.0; if (dim == 'X') { if (meta.getPixelsPhysicalSizeX(0) != null) { value = meta.getPixelsPhysicalSizeX(0).getValue(); } units.append("micrometers\t"); } else if (dim == 'Y') { if (meta.getPixelsPhysicalSizeY(0) != null) { value = meta.getPixelsPhysicalSizeY(0).getValue(); } units.append("micrometers\t"); } else if (dim == 'Z') { if (meta.getPixelsPhysicalSizeZ(0) != null) { value = meta.getPixelsPhysicalSizeZ(0).getValue(); } units.append("micrometers\t"); } else if (dim == 'T') { value = meta.getPixelsTimeIncrement(0); units.append("seconds\t"); } out.writeBytes(value + "\t"); } out.writeBytes("\nparameter\tunits\tbits\t" + units.toString() + "\n"); out.writeBytes("\nend\n"); pixelOffset = out.getFilePointer(); } else if (checkSuffix(currentId, "ics")) { RandomAccessInputStream in = new RandomAccessInputStream(currentId); in.findString("\nend\n"); pixelOffset = in.getFilePointer(); in.close(); } if (checkSuffix(currentId, "ids")) { pixelOffset = 0; } if (pixels == null) { pixels = new RandomAccessOutputStream(currentId); } } /* @see loci.formats.IFormatHandler#close() */ public void close() throws IOException { if (lastPlane != getPlaneCount() - 1 && out != null) { overwriteDimensions(getMetadataRetrieve()); } super.close(); pixelOffset = 0; lastPlane = -1; dimensionOffset = 0; dimensionLength = 0; if (pixels != null) { pixels.close(); } pixels = null; } // -- Helper methods -- private int[] overwriteDimensions(MetadataRetrieve meta) throws IOException { out.seek(dimensionOffset); String order = meta.getPixelsDimensionOrder(series).toString(); int sizeX = meta.getPixelsSizeX(series).getValue().intValue(); int sizeY = meta.getPixelsSizeY(series).getValue().intValue(); int z = meta.getPixelsSizeZ(series).getValue().intValue(); int c = meta.getPixelsSizeC(series).getValue().intValue(); int t = meta.getPixelsSizeT(series).getValue().intValue(); int pixelType = FormatTools.pixelTypeFromString(meta.getPixelsType(series).toString()); int bytesPerPixel = FormatTools.getBytesPerPixel(pixelType); int rgbChannels = getSamplesPerPixel(); if (lastPlane < 0) lastPlane = z * c * t - 1; int[] pos = FormatTools.getZCTCoords(order, z, c, t, z * c * t, lastPlane); lastPlane = -1; StringBuffer dimOrder = new StringBuffer(); int[] sizes = new int[6]; int nextSize = 0; sizes[nextSize++] = 8 * bytesPerPixel; if (rgbChannels > 1) { dimOrder.append("ch\t"); sizes[nextSize++] = pos[1] + 1; } for (int i=0; i<order.length(); i++) { if (order.charAt(i) == 'X') sizes[nextSize++] = sizeX; else if (order.charAt(i) == 'Y') sizes[nextSize++] = sizeY; else if (order.charAt(i) == 'Z') sizes[nextSize++] = pos[0] + 1; else if (order.charAt(i) == 'T') sizes[nextSize++] = pos[2] + 1; else if (order.charAt(i) == 'C' && dimOrder.indexOf("ch") == -1) { sizes[nextSize++] = pos[1] + 1; dimOrder.append("ch"); } if (order.charAt(i) != 'C') { dimOrder.append(String.valueOf(order.charAt(i)).toLowerCase()); } dimOrder.append("\t"); } out.writeBytes("layout\torder\tbits\t" + dimOrder.toString() + "\n"); out.writeBytes("layout\tsizes\t"); for (int i=0; i<sizes.length; i++) { out.writeBytes(sizes[i] + "\t"); } while ((out.getFilePointer() - dimensionOffset) < dimensionLength - 1) { out.writeBytes(" "); } out.writeBytes("\n"); return sizes; } }