/*
* $Id$
*
* Copyright 2011 University of Dundee. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.io.bioformats;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import loci.formats.FormatException;
import loci.formats.in.TiffReader;
import loci.formats.out.TiffWriter;
import loci.formats.tiff.IFD;
/**
* File format writer for OMERO pixels pyramid files.
*
* @author Chris Allan, callan at blackcat dot ca
* @since Beta4.3
*/
public class OmeroPixelsPyramidWriter extends TiffWriter {
/** Logger for this class. */
private final static Logger log =
LoggerFactory.getLogger(OmeroPixelsPyramidWriter.class);
/** Current TIFF image comment for OMERO pixels pyramid TIFFs. */
public static final String IMAGE_DESCRIPTION = "OmeroPixelsPyramid v1.0.0";
/** TIFF tag we're using to store the Bio-Formats series. */
public static final int IFD_TAG_SERIES = 65000;
/** TIFF tag we're using to store the Bio-Formats plane number. */
public static final int IFD_TAG_PLANE_NUMBER = 65001;
/* (non-Javadoc)
* @see loci.formats.out.TiffWriter#close()
*/
@Override
public void close() throws IOException
{
log.debug("close(" + currentId + ")");
try
{
if (currentId != null)
{
postProcess();
}
} catch (FormatException e)
{
String m = "Error during process processing!";
log.error(m, e);
throw new IOException(m);
} finally
{
super.close();
}
}
/* (non-Javadoc)
* @see loci.formats.FormatWriter#setId(java.lang.String)
*/
@Override
public void setId(String id) throws FormatException, IOException
{
log.debug("setId(" + id + ")");
super.setId(id);
}
/**
* Performs re-compression post processing on the pixel pyramid.
* @throws IOException
* @throws FormatException
*/
protected void postProcess() throws IOException, FormatException
{
TiffReader reader = new TiffReader();
try
{
reader.setId(currentId);
// First we want to re-compress resolution level 0 (the source series,
// with resolution levels exposed, are in reverse order).
recompressSeries(reader, 1);
// Second we want to re-compress resolution level 1 (the source series,
// with resolution levels exposed, are in reverse order).
recompressSeries(reader, 2);
} finally {
reader.close();
}
}
/**
* Re-compresses a source series, that is JPEG 2000 compressed, via its
* resolution level.
* @param source Reader created of ourselves.
* @param series Target series for the re-compressed data which is the
* inverse of the source resolution level.
* @throws FormatException
* @throws IOException
*/
protected void recompressSeries(TiffReader source, int series)
throws FormatException, IOException
{
int sourceSeries = source.getSeriesCount() - series;
source.setSeries(sourceSeries);
int imageCount = source.getImageCount();
setSeries(series);
for (int i = 0; i < imageCount; i++)
{
byte[] plane = source.openBytes(i);
IFD ifd = new IFD();
// Ensure that we're compressing all rows of the image in a single
// JPEG 2000 block.
ifd.put(IFD.ROWS_PER_STRIP, new long[] { source.getSizeY() });
// Set the TIFF image description so that we are able to
// differentiate ourselves from basic TIFFs.
ifd.put(IFD.IMAGE_DESCRIPTION, IMAGE_DESCRIPTION);
// First re-usable TIFF IFD (series)
ifd.put(IFD_TAG_SERIES, sourceSeries - 1);
// Second re-usable TIFF IFD (plane number)
ifd.put(IFD_TAG_PLANE_NUMBER, i);
saveBytes(i, plane, ifd);
}
}
}