/* * Copyright 2009 Glencoe Software, Inc. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package ome.io.bioformats; import java.awt.Dimension; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; import java.util.List; import loci.formats.FormatException; import loci.formats.FormatTools; import loci.formats.in.TiffReader; import loci.formats.meta.IMetadata; import loci.formats.out.TiffWriter; import loci.formats.services.OMEXMLService; import loci.formats.tiff.IFD; import loci.formats.tiff.TiffCompression; import ome.conditions.ApiUsageException; import ome.conditions.LockTimeout; import ome.conditions.ResourceError; import ome.io.nio.ConfiguredTileSizes; import ome.io.nio.DimensionsOutOfBoundsException; import ome.io.nio.PixelBuffer; import ome.io.nio.TileSizes; import ome.model.core.Pixels; import ome.util.PixelData; import ome.xml.model.enums.DimensionOrder; import ome.xml.model.enums.EnumerationException; import ome.xml.model.primitives.PositiveInteger; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@link PixelBuffer} implementation which uses Bio-Formats to * read pixels data directly from original files. * * @since OMERO-Beta4.3 */ public class BfPyramidPixelBuffer implements PixelBuffer { private final static Logger log = LoggerFactory.getLogger(BfPyramidPixelBuffer.class); private BfPixelBuffer delegate; /** Bio-Formats implementation used to write to the backing TIFF. */ protected OmeroPixelsPyramidWriter writer; /** * Bio-Formats implementation the delegate uses to read the backing TIFF. */ protected OmeroPixelsPyramidReader reader; /** * File's who absolute path will be passed to * {@link TiffReader#setId(String)} for reading. * * @see {@link #writePath} */ private final File readerFile; /** Description of tile sizes */ private final TileSizes sizes; /** The OMERO pixels set we're backing. */ private final Pixels pixels; /** Last IFD we used during a tile write operation. */ private IFD lastIFD; /** Last z-section offset we used during a tile write operation. */ private int lastZ = -1; /** Last channel offset we used during a tile write operation. */ private int lastC = -1; /** Last timepoint offset we used during a tile write operation. */ private int lastT = -1; /** Metadata implementation used when writing. */ private IMetadata metadata; // LOCKING. See ticket #5083 /** * File whose absolute path will be given to the {@link TiffWriter}. * * This prevents that a partially written file can be accessed, if some * other process does not attempt to acquire the lock. On close, if this is * non-null, then a move from this location to the {@link #filePath} (the * reader path) will be attempted. */ private File writerFile; /** * Lock file used both for the {@link TiffReader} and {@link TiffWriter} * process. */ private File lockFile; /** * {@link RandomAccessFile} opened for the {@link #lockFile} path. */ private RandomAccessFile lockRaf; /** * If not null, {@link FileLock} instance acquired from the {@link #lockRaf} */ private FileLock fileLock; /** The byte order of the compressed pyramid. */ private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN; public static final String PYR_LOCK_EXT = ".pyr_lock"; /** * We may want a constructor that takes the id of an imported file * or that takes a File object? * There should ultimately be some sort of check here that the * file is in a/the repository. * * Upon construction, the pixel buffer is available for reading or writing. * However, on the first read, writing will be subsequently disabled. * * @see <a href="https://trac.openmicroscopy.org/ome/ticket/5083">ticket 5083</a> */ public BfPyramidPixelBuffer(Pixels pixels, String filePath, boolean write) throws IOException, FormatException { this(new ConfiguredTileSizes(), pixels, filePath, write); } /** * Full constructor taking a {@link TileSizes} implementation which defines * how large the pyramid tiles will be. * * @param sizes * @param pixels * @param filePath * @param write * @throws IOException * @throws FormatException */ public BfPyramidPixelBuffer(TileSizes sizes, Pixels pixels, String filePath, boolean write) throws IOException, FormatException { this(sizes, pixels, filePath, write, true); // init! } protected BfPyramidPixelBuffer(TileSizes sizes, Pixels pixels, String filePath, boolean write, boolean init) throws IOException, FormatException { this.sizes = sizes; this.readerFile = new File(filePath); this.pixels = pixels; if (init) { init(filePath, write); } } protected void init(String filePath, boolean write) throws IOException, FormatException { if (!write || readerFile.exists()) { if (write) { log.debug("Initialized in a write-context; setting read-only for " + filePath); } if (!readerFile.exists() && !readerFile.canRead()) { throw new IOException("Cannot access " + filePath); } initializeReader(); } else { final File readerDir = readerFile.getParentFile(); writerFile = File.createTempFile("." + readerFile.getName(), ".tmp", readerDir); writerFile.deleteOnExit(); acquireLock(); } } /** * If the pyramid file exists (which the constructor guarantees) then we * assume that even if a lock file is present, that it's no longer valid. */ protected synchronized void initializeReader() throws IOException, FormatException { File lockFile = lockFile(); if (readerFile.exists() && lockFile.exists()) { // note: we double checked readerFile exists just in case. lockFile.delete(); } reader = new OmeroPixelsPyramidReader(); delegate = new BfPixelBuffer(readerFile.getAbsolutePath(), reader); byteOrder = delegate.isLittleEndian()? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; } /** * Initializes the writer. Since the reader location is not present until * this instance is closed, other {@link BfPyramidPixelBuffer} instances * may try to also call this method in which case {@link #acquireLock()} * will throw a {@link LockTimeout}. * * @param output The file where to write the compressed data. * @param compression The compression to use. * @param bigTiff Pass <code>true</code> to set the <code>bigTiff</code> * flag, <code>false</code> otherwise. * @throws Exception Thrown if an error occurred. */ protected synchronized void initializeWriter(String output, String compression, boolean bigTiff, int tileWidth, int tileLength) throws FormatException { try { if (readerFile.exists()) { throw new ResourceError(" exists. Pyramid is read-only"); } loci.common.services.ServiceFactory lociServiceFactory = new loci.common.services.ServiceFactory(); OMEXMLService service = lociServiceFactory.getInstance(OMEXMLService.class); metadata = service.createOMEXMLMetadata(); addSeries(tileWidth, tileLength); writer = new OmeroPixelsPyramidWriter(); writer.setMetadataRetrieve(metadata); writer.setCompression(compression); writer.setWriteSequentially(true); writer.setInterleaved(true); writer.setBigTiff(bigTiff); writer.setId(output); } catch (Exception e) { throw new FormatException("Error instantiating service.", e); } } /** * Creates a new series for the destination metadata store. * @param metadata Metadata store and retrieve implementation. * @param pixels Source pixels set. * @param series Destination series. * @param sizeX Destination X width. Not necessarily * <code>Pixels.SizeX</code>. * @param sizeY Destination Y height. Not necessarily * <code>Pixels.SizeY</code>. * @throws EnumerationException */ private void createSeries(int series, int sizeX, int sizeY) throws EnumerationException { metadata.setImageID("Image:" + series, series); metadata.setPixelsID("Pixels: " + series, series); metadata.setPixelsBinDataBigEndian( byteOrder == ByteOrder.BIG_ENDIAN? true : false, series, 0); metadata.setPixelsDimensionOrder(DimensionOrder.XYZCT, series); metadata.setPixelsType(ome.xml.model.enums.PixelType.fromString( pixels.getPixelsType().getValue()), series); metadata.setPixelsSizeX(new PositiveInteger(sizeX), series); metadata.setPixelsSizeY(new PositiveInteger(sizeY), series); metadata.setPixelsSizeZ(new PositiveInteger(1), series); metadata.setPixelsSizeC(new PositiveInteger(1), series); int totalPlanes = pixels.getSizeZ() * pixels.getSizeC() * pixels.getSizeT(); metadata.setPixelsSizeT(new PositiveInteger(totalPlanes), series); metadata.setChannelID("Channel:" + series, series, 0); metadata.setChannelSamplesPerPixel(new PositiveInteger(1), series, 0); if (log.isDebugEnabled()) { log.debug(String.format("Added series %d %dx%dx%d", series, sizeX, sizeY, totalPlanes)); } } /** * During tile writing, adds additional all series. * @param tileWidth Tile width of full resolution tiles. * @param tileLength Tile length of full resolution tiles. * @throws EnumerationException */ private void addSeries(int tileWidth, int tileLength) throws EnumerationException { int series = 0; for (int level : new int[] { 0, 5, 4 }) { long imageWidth = pixels.getSizeX(); long imageLength = pixels.getSizeY(); long factor = (long) Math.pow(2, level); long newTileWidth = Math.round((double) tileWidth / factor); newTileWidth = newTileWidth < 1? 1 : newTileWidth; long newTileLength = Math.round((double) tileLength / factor); newTileLength = newTileLength < 1? 1: newTileLength; long evenTilesPerRow = imageWidth / tileWidth; long evenTilesPerColumn = imageLength / tileLength; double remainingWidth = ((double) (imageWidth - (evenTilesPerRow * tileWidth))) / factor; remainingWidth = remainingWidth < 1? Math.ceil(remainingWidth) : Math.round(remainingWidth); double remainingLength = ((double) imageLength - (evenTilesPerColumn * tileLength)) / factor; remainingLength = remainingLength < 1? Math.ceil(remainingLength) : Math.round(remainingLength); int newImageWidth = (int) ((evenTilesPerRow * newTileWidth) + remainingWidth); int newImageLength = (int) ((evenTilesPerColumn * newTileLength) + remainingLength); createSeries(series, newImageWidth, newImageLength); series++; } } protected void acquireLock() { try { lockFile = lockFile(); lockRaf = new RandomAccessFile(lockFile, "rw"); fileLock = lockRaf.getChannel().lock(); // THROWS! } catch (OverlappingFileLockException overlap) { closeRaf(); throw new LockTimeout("Already locked! " + lockFile.getAbsolutePath(), 15*1000, 0); } catch (IOException e) { closeRaf(); throw new LockTimeout("IOException while locking " + lockFile.getAbsolutePath(), 15*1000, 0); } } protected void closeRaf() { if (lockRaf != null) { try { lockRaf.close(); } catch (Exception e) { log.warn("Failed to close " + lockFile, e); } finally { lockRaf = null; } } } protected boolean isLockedByOthers() { if (fileLock != null) { return false; // We control the lock. } // Since we don't control the lock here, we will try to // obtain it and release it immediately. try { lockFile = lockFile(); lockRaf = new RandomAccessFile(lockFile, "rw"); try { fileLock = lockRaf.getChannel().tryLock(); } catch (OverlappingFileLockException ofle) { // Another object in this JVM controls the lock. log.debug("Overlapping file lock exception: " + readerFile); } if (fileLock == null) { // If we don't control the fileLock, then we // also don't have the right to delete the // lockFile. #5655 lockFile = null; return true; } else { return false; } } catch (IOException e) { lockFile = null; throw new RuntimeException(e); } finally { releaseLock(); } } private File lockFile() { File parent = readerFile.getParentFile(); String name = "." + readerFile.getName() + PYR_LOCK_EXT; File lock = new File(parent, name); return lock; } private void releaseLock() { try { if (fileLock != null) { fileLock.release(); } } catch (IOException e) { throw new RuntimeException(e); } finally { fileLock = null; closeRaf(); if (lockFile != null) { lockFile.delete(); lockFile = null; } } } /** * This method should never exit without releasing the lock. */ protected void closeWriter() throws IOException { try { if (writer != null) { writer.close(); writer = null; } } finally { try { if (writerFile != null) { try { FileUtils.moveFile(writerFile, readerFile); } finally { writerFile = null; } } } finally { releaseLock(); } } } /** * Whether or not this instance is in writing-mode. Any of the calls to reader * methods called while this method returns true will close the writer, * saving it to disk and preventing any further write methods. */ public boolean isWrite() { return writerFile != null; } private BfPixelBuffer delegate() { if (isWrite()) { try { closeWriter(); try { initializeReader(); } catch (FormatException e) { throw new RuntimeException(e); } } catch (IOException e1) { throw new RuntimeException(e1); } } else if (delegate == null) { try { initializeReader(); } catch (IOException e) { throw new RuntimeException(e); } catch (FormatException e) { throw new RuntimeException(e); } } return delegate; } /* (non-Javadoc) * @see ome.io.bioformats.BfPixelBuffer#setTile(byte[], java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public synchronized void setTile(byte[] buffer, Integer z, Integer c, Integer t, Integer x, Integer y, Integer w, Integer h) throws IOException, BufferOverflowException { if (!isWrite()) { throw new ApiUsageException("In read-only mode!"); } try { int planeCount = getSizeZ() * getSizeC() * getSizeT(); int planeNumber = FormatTools.getIndex( "XYZCT", getSizeZ(), getSizeC(), getSizeT(), planeCount, z, c, t); IFD ifd = getIFD(z, c, t, w, h); if (log.isDebugEnabled()) { log.debug(String.format( "Writing tile planeNumber:%d bufferSize:%d ifd:%s " + "x:%d y:%d w:%d h:%d", planeNumber, buffer.length, ifd.toString(), x, y, w, h)); } writer.saveBytes(planeNumber, buffer, ifd, x, y, w, h); } catch (FormatException e) { throw new RuntimeException(e); } } /** * Retrieves the IFD that should be used for a given planar offset. * @param z Z-section offset requested. * @param c Channel offset requested. * @param t Timepoint offset requested. * @param w Tile width requested. * @param h Tile height requested. * @return A new or already allocated IFD for use when writing tiles. */ private synchronized IFD getIFD(int z, int c, int t, int w, int h) { if (lastT == -1 && lastC == -1 && lastZ == -1) { try { initializeWriter(writerFile.getAbsolutePath(), TiffCompression.JPEG_2000.getCodecName(), true, w, h); } catch (Exception e) { throw new RuntimeException(e); } } if (lastT != t || lastC != c || lastZ != z) { lastIFD = new IFD(); lastIFD.put(IFD.IMAGE_DESCRIPTION, OmeroPixelsPyramidWriter.IMAGE_DESCRIPTION); lastIFD.put(IFD.TILE_WIDTH, w); lastIFD.put(IFD.TILE_LENGTH, h); if (log.isDebugEnabled()) { log.debug(String.format( "Creating new IFD z:%d c:%d t:%d w:%d: h:%d -- %s", z, c, t, w, h, lastIFD)); } } lastT = t; lastC = c; lastZ = z; return lastIFD; } /** * Retrieves the rasterized timepoint offset based on the linearization of * the z-section, channel and timepoint offsets. * @param z Z-section offset requested. * @param c Channel offset requested. * @param t Timepoint offset requested. * @return */ private int getRasterizedT(int z, int c, int t) { int rasterizedT = (t * pixels.getSizeC() * pixels.getSizeZ()) // T + (c * pixels.getSizeZ()) // C + z; // Z if (log.isDebugEnabled()) { log.debug(String.format("Rasterizing z:%d c:%d t:%d to t:%d", z, c, t, rasterizedT)); } return rasterizedT; } /** * Checks that the tile parameters are not weirdly offset and do not have * odd sizes. * @param x X offset to the tile request. * @param y Y offset to the tile request. * @param w Width of the tile request. * @param h Height of the tile request. * @throws IOException If there is a problem with the parameters or a * problem checking them. */ private synchronized void checkTileParameters(int x, int y, int w, int h) throws IOException { // No-op. } /** * Returns the current pixel byte order. * @return See above. */ public ByteOrder getByteOrder() { return byteOrder; } /** * Sets the pixel byte order. * @param byteOrder The pixel byte order to set. */ public void setByteOrder(ByteOrder byteOrder) { this.byteOrder = byteOrder; } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#calculateMessageDigest() */ public synchronized byte[] calculateMessageDigest() throws IOException { return delegate().calculateMessageDigest(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#checkBounds(java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public synchronized void checkBounds(Integer x, Integer y, Integer z, Integer c, Integer t) throws DimensionsOutOfBoundsException { t = getRasterizedT(z, c, t); c = 0; z = 0; delegate().checkBounds(x, y, z, c, t); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#close() */ public synchronized void close() throws IOException { try { if (delegate != null) { delegate.close(); } } catch (IOException e) { log.error("Failure to close delegate.", e); } delegate = null; if (reader != null) { try { reader.close(); } catch (Exception e) { log.warn("Failed to close reader", e); } finally { reader = null; } } closeWriter(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getByteWidth() */ public synchronized int getByteWidth() { return delegate().getByteWidth(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getCol(java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public synchronized PixelData getCol(Integer x, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { t = getRasterizedT(z, c, t); c = 0; z = 0; PixelData data = delegate().getCol(x, z, c, t); data.setOrder(byteOrder); return data; } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getColDirect(java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, byte[]) */ public synchronized byte[] getColDirect(Integer x, Integer z, Integer c, Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { t = getRasterizedT(z, c, t); c = 0; z = 0; return delegate().getColDirect(x, z, c, t, buffer); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getColSize() */ public synchronized Integer getColSize() { return delegate().getColSize(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getHypercube(java.util.List, java.util.List, java.util.List) */ public synchronized PixelData getHypercube(List<Integer> offset, List<Integer> size, List<Integer> step) throws IOException, DimensionsOutOfBoundsException { throw new UnsupportedOperationException("Not yet implemented."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getHypercubeDirect(java.util.List, java.util.List, java.util.List, byte[]) */ public synchronized byte[] getHypercubeDirect(List<Integer> offset, List<Integer> size, List<Integer> step, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { throw new UnsupportedOperationException("Not yet implemented."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getHypercubeSize(java.util.List, java.util.List, java.util.List) */ public synchronized Long getHypercubeSize(List<Integer> offset, List<Integer> size, List<Integer> step) throws DimensionsOutOfBoundsException { throw new UnsupportedOperationException("Not yet implemented."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getId() */ public synchronized long getId() { return delegate().getId(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getPath() */ public synchronized String getPath() { return delegate().getPath(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getPlane(java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public synchronized PixelData getPlane(Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { t = getRasterizedT(z, c, t); c = 0; z = 0; PixelData data = delegate().getPlane(z, c, t); data.setOrder(byteOrder); return data; } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getPlaneDirect(java.lang.Integer, java.lang.Integer, java.lang.Integer, byte[]) */ public synchronized byte[] getPlaneDirect(Integer z, Integer c, Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { t = getRasterizedT(z, c, t); c = 0; z = 0; return delegate().getPlaneDirect(z, c, t, buffer); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getPlaneOffset(java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public synchronized Long getPlaneOffset(Integer z, Integer c, Integer t) throws DimensionsOutOfBoundsException { t = getRasterizedT(z, c, t); c = 0; z = 0; return delegate().getPlaneOffset(z, c, t); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getPlaneRegion(java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public synchronized PixelData getPlaneRegion(Integer x, Integer y, Integer width, Integer height, Integer z, Integer c, Integer t, Integer stride) throws IOException, DimensionsOutOfBoundsException { t = getRasterizedT(z, c, t); c = 0; z = 0; PixelData data = delegate().getPlaneRegion(x, y, width, height, z, c, t, stride); data.setOrder(byteOrder); return data; } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getPlaneRegionDirect(java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, byte[]) */ public synchronized byte[] getPlaneRegionDirect(Integer z, Integer c, Integer t, Integer count, Integer offset, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { t = getRasterizedT(z, c, t); c = 0; z = 0; return getPlaneRegionDirect(z, c, t, count, offset, buffer); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getPlaneSize() */ public synchronized Long getPlaneSize() { return delegate().getPlaneSize(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getRegion(java.lang.Integer, java.lang.Long) */ public PixelData getRegion(Integer size, Long offset) throws IOException { throw new UnsupportedOperationException("Not supported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getRegionDirect(java.lang.Integer, java.lang.Long, byte[]) */ public byte[] getRegionDirect(Integer size, Long offset, byte[] buffer) throws IOException { throw new UnsupportedOperationException("Not supported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getRow(java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public synchronized PixelData getRow(Integer y, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { t = getRasterizedT(z, c, t); c = 0; z = 0; PixelData data = delegate().getRow(y, z, c, t); data.setOrder(byteOrder); return data; } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getRowDirect(java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, byte[]) */ public synchronized byte[] getRowDirect(Integer y, Integer z, Integer c, Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { t = getRasterizedT(z, c, t); c = 0; z = 0; return delegate().getRowDirect(y, z, c, t, buffer); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getRowOffset(java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public synchronized Long getRowOffset(Integer y, Integer z, Integer c, Integer t) throws DimensionsOutOfBoundsException { t = getRasterizedT(z, c, t); c = 0; z = 0; return delegate().getRowOffset(y, z, c, t); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getRowSize() */ public synchronized Integer getRowSize() { return delegate().getRowSize(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getSizeC() */ public int getSizeC() { // Not delegating due to the timepoint rasterization of dimensions // that's happening below us. return pixels.getSizeC(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getSizeT() */ public int getSizeT() { // Not delegating due to the timepoint rasterization of dimensions // that's happening below us. return pixels.getSizeT(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getSizeX() */ public synchronized int getSizeX() { if (delegate == null || delegate.reader.get() == null) { // The downstream reader has not been initialized, we don't need to // delegate and can't even if we wanted to because no data has // actually been written yet. return pixels.getSizeX(); } return delegate.getSizeX(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getSizeY() */ public synchronized int getSizeY() { if (delegate == null || delegate.reader.get() == null) { // The downstream reader has not been initialized, we don't need to // delegate and can't even if we wanted to because no data has // actually been written yet. return pixels.getSizeY(); } return delegate.getSizeY(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getSizeZ() */ public int getSizeZ() { // Not delegating due to the timepoint rasterization of dimensions // that's happening below us. return pixels.getSizeZ(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getStack(java.lang.Integer, java.lang.Integer) */ public PixelData getStack(Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { throw new UnsupportedOperationException("Not supported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getStackDirect(java.lang.Integer, java.lang.Integer, byte[]) */ public byte[] getStackDirect(Integer c, Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { throw new UnsupportedOperationException("Not supported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getStackOffset(java.lang.Integer, java.lang.Integer) */ public Long getStackOffset(Integer c, Integer t) throws DimensionsOutOfBoundsException { throw new UnsupportedOperationException("Not supported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getStackSize() */ public synchronized Long getStackSize() { return delegate().getStackSize(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getTile(java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public synchronized PixelData getTile(Integer z, Integer c, Integer t, Integer x, Integer y, Integer w, Integer h) throws IOException { checkTileParameters(x, y, w, h); t = getRasterizedT(z, c, t); c = 0; z = 0; PixelData data = delegate().getTile(z, c, t, x, y, w, h); data.setOrder(byteOrder); return data; } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getTileDirect(java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, byte[]) */ public synchronized byte[] getTileDirect(Integer z, Integer c, Integer t, Integer x, Integer y, Integer w, Integer h, byte[] buffer) throws IOException { checkTileParameters(x, y, w, h); t = getRasterizedT(z, c, t); c = 0; z = 0; return delegate().getTileDirect(z, c, t, x, y, w, h, buffer); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getTimepoint(java.lang.Integer) */ public synchronized PixelData getTimepoint(Integer t) throws IOException, DimensionsOutOfBoundsException { throw new UnsupportedOperationException("Not supported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getTimepointDirect(java.lang.Integer, byte[]) */ public synchronized byte[] getTimepointDirect(Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { throw new UnsupportedOperationException("Not supported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getTimepointOffset(java.lang.Integer) */ public Long getTimepointOffset(Integer t) throws DimensionsOutOfBoundsException { throw new UnsupportedOperationException("Not supported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getTimepointSize() */ public synchronized Long getTimepointSize() { return delegate().getTimepointSize(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getTotalSize() */ public synchronized Long getTotalSize() { return delegate().getTotalSize(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#isFloat() */ public synchronized boolean isFloat() { return delegate().isFloat(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#isSigned() */ public synchronized boolean isSigned() { return delegate().isSigned(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setPlane(java.nio.ByteBuffer, java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public void setPlane(ByteBuffer buffer, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException( "Non-tile based writing unsupported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setPlane(byte[], java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public void setPlane(byte[] buffer, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException( "Non-tile based writing unsupported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setRegion(java.lang.Integer, java.lang.Long, byte[]) */ public void setRegion(Integer size, Long offset, byte[] buffer) throws IOException, BufferOverflowException { throw new UnsupportedOperationException( "Non-tile based writing unsupported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setRegion(java.lang.Integer, java.lang.Long, java.nio.ByteBuffer) */ public void setRegion(Integer size, Long offset, ByteBuffer buffer) throws IOException, BufferOverflowException { throw new UnsupportedOperationException( "Non-tile based writing unsupported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setRow(java.nio.ByteBuffer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public void setRow(ByteBuffer buffer, Integer y, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException( "Non-tile based writing unsupported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setStack(java.nio.ByteBuffer, java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public void setStack(ByteBuffer buffer, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException( "Non-tile based writing unsupported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setStack(byte[], java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public void setStack(byte[] buffer, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException( "Non-tile based writing unsupported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setTimepoint(java.nio.ByteBuffer, java.lang.Integer) */ public void setTimepoint(ByteBuffer buffer, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException( "Non-tile based writing unsupported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setTimepoint(byte[], java.lang.Integer) */ public void setTimepoint(byte[] buffer, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException( "Non-tile based writing unsupported."); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getResolutionLevel() */ public synchronized int getResolutionLevel() { if (isWrite()) { throw new ApiUsageException("In write mode!"); } return delegate().getResolutionLevel(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getResolutionLevels() */ public synchronized int getResolutionLevels() { if (isWrite()) { throw new ApiUsageException("In write mode!"); } return delegate().getResolutionLevels(); } public synchronized List<List<Integer>> getResolutionDescriptions() { if (isWrite()) { throw new ApiUsageException("In write mode!"); } return delegate().getResolutionDescriptions(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getTileSize() */ public synchronized Dimension getTileSize() { if (isWrite()) { return new Dimension(sizes.getTileWidth(), sizes.getTileHeight()); } return delegate().getTileSize(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setResolutionLevel(int) */ public synchronized void setResolutionLevel(int resolutionLevel) { if (isWrite()) { throw new ApiUsageException("In write mode!"); } delegate().setResolutionLevel(resolutionLevel); } }