/* * ome.io.nio.PixelBuffer * * Copyright 2007-2013 Glencoe Software Inc. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package ome.io.nio; import java.awt.Dimension; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import ome.conditions.ApiUsageException; import ome.model.core.Pixels; import ome.util.PixelData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class implementation of the PixelBuffer interface for standard "proprietary" * ROMIO/OMEIS data format. * * @author Chris Allan      <a * href="mailto:chris@glencoesoftware.com">chris@glencoesoftware.com</a> * @version $Revision$ * @since 3.0 * @see PixelBuffer */ public class RomioPixelBuffer extends AbstractBuffer implements PixelBuffer { /** The logger for this particular class */ private static Logger log = LoggerFactory.getLogger(RomioPixelBuffer.class); /** Default maximum buffer size for planar data transfer. (1MB) */ public static final int MAXIMUM_BUFFER_SIZE = 1048576; /** Reference to the pixels. */ private Pixels pixels; private RandomAccessFile file; private FileChannel channel; /** The size of a row. */ private Integer rowSize; /** The size of a column. */ private Integer colSize; /** The size of a plane. */ private Long planeSize; /** The size of a stack. */ private Long stackSize; /** The size of a timepoint. */ private Long timepointSize; /** The total size. */ private Long totalSize; /** * Whether or not any of the mutators to write data can be called. * If not, an internal exception will be raised since it represents * a programming error. */ private final boolean permitModification; /** * Creates a new instance. {@link #permitModification} defaults to false. * * @param path The path to the file. * @param pixels The pixels object to handle. */ public RomioPixelBuffer(String path, Pixels pixels) { this(path, pixels, false); } /** * Creates a new instance, with manual setting of {@link #permitModification}. * * @param path The path to the file. * @param pixels The pixels object to handle. */ public RomioPixelBuffer(String path, Pixels pixels, boolean permitModification) { super(path); if (pixels == null) { throw new NullPointerException( "Expecting a not-null pixels element."); } this.pixels = pixels; this.permitModification = permitModification; } private void throwIfReadOnly() { if (!permitModification) { throw new ApiUsageException("Write-method not permitted."); } } /** * Converts a Long value to an Integer safely. * @param v Long value to convert. * @throws ApiUsageException If the conversion would cause an overflow * or an underflow. */ public static Integer safeLongToInteger(Long v) { if (v > Integer.MAX_VALUE) { throw new ApiUsageException(String.format( "Converting Long %d to Integer is an overflow.", v)); } if (v < Integer.MIN_VALUE) { throw new ApiUsageException(String.format( "Converting Long %d to Integer is an underflow.", v)); } return v.intValue(); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#checkBounds(Integer, Integer, Integer, Integer, Integer) */ public void checkBounds(Integer x, Integer y, Integer z, Integer c, Integer t) throws DimensionsOutOfBoundsException { if (x != null && (x > getSizeX() - 1 || x < 0)) { throw new DimensionsOutOfBoundsException("X '" + x + "' greater than sizeX '" + getSizeX() + "'."); } if (y != null && (y > getSizeY() - 1 || y < 0)) { throw new DimensionsOutOfBoundsException("Y '" + y + "' greater than sizeY '" + getSizeY() + "'."); } if (z != null && (z > getSizeZ() - 1 || z < 0)) { throw new DimensionsOutOfBoundsException("Z '" + z + "' greater than sizeZ '" + getSizeZ() + "'."); } if (c != null && (c > getSizeC() - 1 || c < 0)) { throw new DimensionsOutOfBoundsException("C '" + c + "' greater than sizeC '" + getSizeC() + "'."); } if (t != null && (t > getSizeT() - 1 || t < 0)) { throw new DimensionsOutOfBoundsException("T '" + t + "' greater than sizeT '" + getSizeT() + "'."); } } private FileChannel getFileChannel() throws FileNotFoundException { if (channel == null) { file = new RandomAccessFile(getPath(), "rw"); channel = file.getChannel(); } return channel; } /** * Closes the buffer, cleaning up file state. * * @throws IOException * if an I/O error occurs. */ public void close() throws IOException { if (channel != null) { try { channel.close(); } catch (Exception e) { log.error("Error closing channel", e); } finally { channel = null; } } if (file != null) { try { file.close(); } catch (Exception e) { log.error("Error closing file", e); } finally { file = null; } } } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getPlaneSize() */ public Long getPlaneSize() { if (planeSize == null) { planeSize = (long) getSizeX() * (long) getSizeY() * getByteWidth(); } return planeSize; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getRowSize() */ public Integer getRowSize() { if (rowSize == null) { rowSize = getSizeX() * getByteWidth(); } return rowSize; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getColSize() */ public Integer getColSize() { if (colSize == null) { colSize = getSizeY() * getByteWidth(); } return colSize; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getStackSize() */ public Long getStackSize() { if (stackSize == null) { stackSize = getPlaneSize() * getSizeZ(); } return stackSize; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getTimepointSize() */ public Long getTimepointSize() { if (timepointSize == null) { timepointSize = getStackSize() * getSizeC(); } return timepointSize; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getTotalSize() */ public Long getTotalSize() { if (totalSize == null) { totalSize = getTimepointSize() * getSizeT(); } return totalSize; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getRowOffset(Integer, Integer, Integer, Integer) */ public Long getRowOffset(Integer y, Integer z, Integer c, Integer t) throws DimensionsOutOfBoundsException { checkBounds(null, y, z, c, t); Integer rowSize = getRowSize(); Long timepointSize = getTimepointSize(); Long stackSize = getStackSize(); Long planeSize = getPlaneSize(); return (long) rowSize * y + timepointSize * t + stackSize * c + planeSize * z; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getPlaneOffset(Integer, Integer, Integer) */ public Long getPlaneOffset(Integer z, Integer c, Integer t) throws DimensionsOutOfBoundsException { checkBounds(null, null, z, c, t); Long timepointSize = getTimepointSize(); Long stackSize = getStackSize(); Long planeSize = getPlaneSize(); return timepointSize * t + stackSize * c + planeSize * z; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getStackOffset(Integer, Integer) */ public Long getStackOffset(Integer c, Integer t) throws DimensionsOutOfBoundsException { checkBounds(null, null, null, c, t); Long timepointSize = getTimepointSize(); Long stackSize = getStackSize(); return timepointSize * t + stackSize * c; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getTimepointOffset(Integer) */ public Long getTimepointOffset(Integer t) throws DimensionsOutOfBoundsException { checkBounds(null, null, null, null, t); Long timepointSize = getTimepointSize(); return (long) timepointSize * t; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getRegion(Integer, Long) */ public PixelData getRegion(Integer size, Long offset) throws IOException { FileChannel fileChannel = getFileChannel(); /* * fileChannel should not be "null" as it will throw an exception if * there happens to be an error. */ MappedByteBuffer b = fileChannel.map(MapMode.READ_ONLY, offset, size); return new PixelData(pixels.getPixelsType().getValue(), b); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getRegionDirect(Integer, Long, byte[]) */ public byte[] getRegionDirect(Integer size, Long offset, byte[] buffer) throws IOException { if (buffer.length != size) throw new ApiUsageException("Buffer size incorrect."); final PixelData pd = getRegion(size, offset); pd.getData().get(buffer); pd.dispose(); return buffer; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getRow(Integer, Integer, Integer, Integer) */ public PixelData getRow(Integer y, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { //dimension check getRowOffset Long offset = getRowOffset(y, z, c, t); Integer size = getRowSize(); return getRegion(size, offset); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getCol(Integer, Integer, Integer, Integer) */ public PixelData getCol(Integer x, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { //Dimension check in plane. PixelData plane = getPlane(z, c, t); Integer sizeY = getSizeY(); Integer sizeX = getSizeX(); Integer colSize = getColSize(); ByteBuffer buf = ByteBuffer.wrap(new byte[colSize]); PixelData column = new PixelData(pixels.getPixelsType().getValue(), buf); int offset; double value; for (int i = 0; i < sizeY; i++) { offset = (i * sizeX) + x; value = plane.getPixelValue(offset); column.setPixelValue(i, value); } plane.dispose(); return column; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getRowDirect(Integer, Integer, Integer, Integer, byte[]) */ public byte[] getRowDirect(Integer y, Integer z, Integer c, Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { if (buffer.length != getRowSize()) throw new ApiUsageException("Buffer size incorrect."); final PixelData pd = getRow(y, z, c, t); pd.getData().get(buffer); pd.dispose(); return buffer; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getColDirect(Integer, Integer, Integer, Integer, byte[]) */ public byte[] getColDirect(Integer x, Integer z, Integer c, Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { PixelData plane = getPlane(z, c, t); Integer sizeY = getSizeY(); Integer sizeX = getSizeX(); ByteBuffer buf = ByteBuffer.wrap(buffer); PixelData column = new PixelData(pixels.getPixelsType().getValue(), buf); int offset; double value; for (int i = 0; i < sizeY; i++) { offset = (i * sizeX) + x; value = plane.getPixelValue(offset); column.setPixelValue(i, value); } plane.dispose(); return buffer; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getHypercube(List, List, List) */ public PixelData getHypercube(List<Integer> offset, List<Integer> size, List<Integer> step) throws IOException, DimensionsOutOfBoundsException { byte[] buffer = new byte[ safeLongToInteger(getHypercubeSize(offset,size,step))]; getHypercubeDirect(offset,size,step,buffer); return new PixelData(pixels.getPixelsType().getValue(), ByteBuffer.wrap(buffer)); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getHypercubeDirect(List, List, List, byte[]) */ public byte[] getHypercubeDirect(List<Integer> offset, List<Integer> size, List<Integer> step, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { if (buffer.length != getHypercubeSize(offset, size, step)) throw new RuntimeException("Buffer size incorrect."); getWholeHypercube(offset,size,step,buffer); return buffer; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getPlaneRegionDirect(Integer, Integer, Integer, Integer, * Integer, byte[]) */ public byte[] getPlaneRegionDirect(Integer z, Integer c, Integer t, Integer count, Integer offset, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { final PixelData pd = getPlane(z, c, t); final ByteBuffer b = pd.getData(); b.position(offset * getByteWidth()); b.get(buffer, 0, count * getByteWidth()); pd.dispose(); return buffer; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getPlane(Integer, Integer, Integer) */ public PixelData getPlane(Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { log.info("Retrieving plane: " + z + "x" + c + "x" + t); Long offset = getPlaneOffset(z, c, t); Integer size = safeLongToInteger(getPlaneSize()); PixelData region = getRegion(size, offset); byte[] nullPlane = PixelsService.nullPlane; for (int i = 0; i < PixelsService.NULL_PLANE_SIZE; i++) { if (region.getData().get(i) != nullPlane[i]) { return region; } } region.dispose(); return null; // All of the nullPlane bytes match, non-filled plane } /** * Implemented as specified by {@link ByteBuffer} I/F. * @see PixelBuffer#getPlaneRegion(Integer, Integer, Integer, Integer, * Integer, Integer, Integer, Integer) */ public PixelData getPlaneRegion(Integer x, Integer y, Integer width, Integer height, Integer z, Integer c, Integer t, Integer stride) throws IOException, DimensionsOutOfBoundsException { if (stride == null || stride < 0) stride = 0; checkBounds(x, y, z, c, t); checkBounds(x+width-1, y+height-1, null, null, null); PixelData plane = getPlane(z, c, t); Integer size; ByteBuffer buf; PixelData region = null; int offset; if (stride == 0) { size = width*height*getByteWidth(); buf = ByteBuffer.wrap(new byte[size]); region = new PixelData(pixels.getPixelsType().getValue(), buf); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { offset = (i+y)*getSizeX()+x+j; region.setPixelValue(i*width+j, plane.getPixelValue(offset)); } } plane.dispose(); return region; } stride++; int w = width/stride; size = width*height*getByteWidth()/(stride*stride); buf = ByteBuffer.wrap(new byte[size]); region = new PixelData(pixels.getPixelsType().getValue(), buf); int k = 0; int l = 0; for (int i = 0; i < height; i = i+stride) { l = 0; for (int j = 0; j < width; j = j+stride) { offset = (i+y)*getSizeX()+x+j; region.setPixelValue(k*w+l, plane.getPixelValue(offset)); l++; } k++; } plane.dispose(); return region; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getPlaneDirect(Integer, Integer, Integer, byte[]) */ public byte[] getPlaneDirect(Integer z, Integer c, Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { if (buffer.length != getPlaneSize()) throw new ApiUsageException("Buffer size incorrect."); final PixelData pd = getPlane(z, c, t); pd.getData().get(buffer); pd.dispose(); return buffer; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getStack(Integer, Integer) */ public PixelData getStack(Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { Long offset = getStackOffset(c, t); Integer size = safeLongToInteger(getStackSize()); return getRegion(size, offset); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getStackDirect(Integer, Integer, byte[]) */ public byte[] getStackDirect(Integer c, Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { if (buffer.length != getStackSize()) throw new ApiUsageException("Buffer size incorrect."); final PixelData pd = getStack(c, t); pd.getData().get(buffer); pd.dispose(); return buffer; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getTimepoint(Integer) */ public PixelData getTimepoint(Integer t) throws IOException, DimensionsOutOfBoundsException { Long offset = getTimepointOffset(t); Integer size = safeLongToInteger(getTimepointSize()); return getRegion(size, offset); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getTimepointDirect(Integer, byte[]) */ public byte[] getTimepointDirect(Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { if (buffer.length != getTimepointSize()) throw new ApiUsageException("Buffer size incorrect."); final PixelData pd = getTimepoint(t); pd.getData().get(buffer); pd.dispose(); return buffer; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#setRegion(Integer, Long, byte[]) */ public void setRegion(Integer size, Long offset, byte[] buffer) throws IOException { throwIfReadOnly(); ByteBuffer buf = MappedByteBuffer.wrap(buffer); buf.limit(size); setRegion(size, offset, buf); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#setRegion(Integer, Long, ByteBuffer) */ public void setRegion(Integer size, Long offset, ByteBuffer buffer) throws IOException { throwIfReadOnly(); FileChannel fileChannel = getFileChannel(); /* * fileChannel should not be "null" as it will throw an exception if * there happens to be an error. */ fileChannel.write(buffer, offset); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#setRow(ByteBuffer, Integer, Integer, Integer, Integer) */ public void setRow(ByteBuffer buffer, Integer y, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { throwIfReadOnly(); Long offset = getRowOffset(y, z, c, t); Integer size = getRowSize(); setRegion(size, offset, buffer); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#setPlane(ByteBuffer, Integer, Integer, Integer) */ public void setPlane(ByteBuffer buffer, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { throwIfReadOnly(); Long offset = getPlaneOffset(z, c, t); Integer size = safeLongToInteger(getPlaneSize()); if (buffer.limit() != size) { // Handle the size mismatch. if (buffer.limit() < size) throw new BufferUnderflowException(); throw new BufferOverflowException(); } setRegion(size, offset, buffer); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#setPlane(byte[], Integer, Integer, Integer) */ public void setPlane(byte[] buffer, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { throwIfReadOnly(); setPlane(ByteBuffer.wrap(buffer), z, c, t); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#setStack(ByteBuffer, Integer, Integer, Integer) */ public void setStack(ByteBuffer buffer, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { throwIfReadOnly(); Long offset = getStackOffset(c, t); Integer size = safeLongToInteger(getStackSize()); if (buffer.limit() != size) { // Handle the size mismatch. if (buffer.limit() < size) throw new BufferUnderflowException(); throw new BufferOverflowException(); } setRegion(size, offset, buffer); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#setStack(byte[], Integer, Integer, Integer) */ public void setStack(byte[] buffer, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { throwIfReadOnly(); setStack(MappedByteBuffer.wrap(buffer), z, c, t); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#setTimepoint(ByteBuffer, Integer) */ public void setTimepoint(ByteBuffer buffer, Integer t) throws IOException, DimensionsOutOfBoundsException { throwIfReadOnly(); Long offset = getTimepointOffset(t); Integer size = safeLongToInteger(getTimepointSize()); if (buffer.limit() != size) { // Handle the size mismatch. if (buffer.limit() < size) throw new BufferUnderflowException(); throw new BufferOverflowException(); } setRegion(size, offset, buffer); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#setTimepoint(ByteBuffer, Integer) */ public void setTimepoint(byte[] buffer, Integer t) throws IOException, DimensionsOutOfBoundsException { throwIfReadOnly(); setTimepoint(MappedByteBuffer.wrap(buffer), t); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#calculateMessageDigest() */ public byte[] calculateMessageDigest() throws IOException { MessageDigest md; try { md = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException( "Required SHA-1 message digest algorithm unavailable."); } for (int t = 0; t < getSizeT(); t++) { for (int c = 0; c < getSizeC(); c++) { for (int z = 0; z < getSizeZ(); z++) { try { final PixelData pd = getPlane(z, c, t); md.update(pd.getData()); pd.dispose(); } catch (DimensionsOutOfBoundsException e) { // This better not happen. :) throw new RuntimeException(e); } } } } return md.digest(); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getByteWidth() */ public int getByteWidth() { return PixelData.getBitDepth(pixels.getPixelsType().getValue()) / 8; } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#isSigned() */ public boolean isSigned() { PixelData d = new PixelData(pixels.getPixelsType().getValue(), null); return d.isSigned(); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#isFloat() */ public boolean isFloat() { PixelData d = new PixelData(pixels.getPixelsType().getValue(), null); return d.isFloat(); } // // Delegate methods to ease work with pixels // /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getSizeC() */ public int getSizeC() { return pixels.getSizeC(); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getSizeT() */ public int getSizeT() { return pixels.getSizeT(); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getSizeX() */ public int getSizeX() { return pixels.getSizeX(); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getSizeY() */ public int getSizeY() { return pixels.getSizeY(); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getSizeZ() */ public int getSizeZ() { return pixels.getSizeZ(); } /** * Implemented as specified by {@link PixelBuffer} I/F. * @see PixelBuffer#getId() */ public long getId() { return pixels.getId(); } public String getSha1() { return pixels.getSha1(); } /* (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 PixelData getTile(Integer z, Integer c, Integer t, Integer x, Integer y, Integer w, Integer h) throws IOException { return getPlaneRegion(x, y, w, h, z, c, t, 0); } /* (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 byte[] getTileDirect(Integer z, Integer c, Integer t, Integer x, Integer y, Integer w, Integer h, byte[] buffer) throws IOException { List<Integer> offset = Arrays.asList(new Integer[]{x,y,z,c,t}); List<Integer> size = Arrays.asList(new Integer[]{w,h,1,1,1}); List<Integer> step = Arrays.asList(new Integer[]{1,1,1,1,1}); return getHypercubeDirect(offset, size, step, buffer); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setTile(byte[], java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer, java.lang.Integer) */ public void setTile(byte[] buffer, Integer z, Integer c, Integer t, Integer x, Integer y, Integer w, Integer h) throws IOException, BufferOverflowException { if (x != 0) { throw new UnsupportedOperationException( "ROMIO pixel buffer only supports 0 offseted tile writes."); } if (w != getSizeX()) { throw new UnsupportedOperationException( "ROMIO pixel buffer only supports full row writes."); } try { long offset = getPlaneOffset(z, c, t); offset += getByteWidth() * getSizeX() * y; setRegion(buffer.length, offset, buffer); } catch (DimensionsOutOfBoundsException e) { throw new RuntimeException(e); } } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getResolutionLevel() */ public int getResolutionLevel() { return 0; } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getResolutionLevels() */ public int getResolutionLevels() { return 1; } public List<List<Integer>> getResolutionDescriptions() { List<Integer> sizes = Arrays.asList(getSizeX(), getSizeY()); List<List<Integer>> rv = new ArrayList<List<Integer>>(); rv.add(sizes); return rv; } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getTileSize() */ public Dimension getTileSize() { int width = getSizeX(); int height = Math.min(getSizeY(), (MAXIMUM_BUFFER_SIZE / getByteWidth()) / getSizeX()); return new Dimension(width, height); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setResolutionLevel(int) */ public void setResolutionLevel(int resolutionLevel) { throw new UnsupportedOperationException( "Cannot set resolution levels on a ROMIO pixel buffer."); } public Long getHypercubeSize(List<Integer> offset, List<Integer> size, List<Integer> step) throws DimensionsOutOfBoundsException { // only works for 5d at present checkCubeBounds(offset, size, step); int tStripes = (size.get(4) + step.get(4) - 1) / step.get(4); int cStripes = (size.get(3) + step.get(3) - 1) / step.get(3); int zStripes = (size.get(2) + step.get(2) - 1) / step.get(2); int yStripes = (size.get(1) + step.get(1) - 1) / step.get(1); int xStripes = (size.get(0) + step.get(0) - 1) / step.get(0); long tileRowSize = getByteWidth() * (long) xStripes; long cubeSize = tileRowSize * yStripes * zStripes * cStripes * tStripes; return cubeSize; } /* * Temporary helpers. May be factored out. * This code is repeated in bfPixelWrapper and so needs refactoring. */ private byte[] getWholeHypercube(List<Integer> offset, List<Integer> size, List<Integer> step, byte[] cube) throws IOException { int cubeOffset = 0; int xStripes = (size.get(0) + step.get(0) - 1) / step.get(0); int pixelSize = getByteWidth(); int tileRowSize = pixelSize * xStripes; byte[] plane = new byte[safeLongToInteger(getPlaneSize())]; for(int t = offset.get(4); t < size.get(4)+offset.get(4); t += step.get(4)) { for(int c = offset.get(3); c < size.get(3)+offset.get(3); c += step.get(3)) { for(int z = offset.get(2); z < size.get(2)+offset.get(2); z += step.get(2)) { getPlaneDirect(z,c,t,plane); int rowOffset = offset.get(1)*getRowSize(); if(step.get(0)==1) { int byteOffset = rowOffset + offset.get(0)*pixelSize; for(int y = offset.get(1); y < size.get(1)+offset.get(1); y += step.get(1)) { System.arraycopy(plane, byteOffset, cube, cubeOffset, tileRowSize); cubeOffset += tileRowSize; byteOffset += getRowSize()*step.get(1); } } else { for(int y = offset.get(1); y < size.get(1)+offset.get(1); y += step.get(1)) { int byteOffset = offset.get(0)*pixelSize; for(int x = offset.get(0); x < size.get(0)+offset.get(0); x += step.get(0)) { System.arraycopy(plane, rowOffset+byteOffset, cube, cubeOffset, pixelSize); cubeOffset += pixelSize; byteOffset += step.get(0)*pixelSize; } rowOffset += getRowSize()*step.get(1); } } } } } return cube; } private void checkCubeBounds(List<Integer> offset, List<Integer> size, List<Integer> step) throws DimensionsOutOfBoundsException { // At the moment the array must contain 5 values if(offset.size()!=5 || size.size()!=5 || step.size()!=5) { throw new DimensionsOutOfBoundsException( "Invalid List length: each list must contain 5 elements XYZCT"); } checkBounds(offset.get(0),offset.get(1),offset.get(2),offset.get(3),offset.get(4)); checkBounds(offset.get(0)+size.get(0)-1,offset.get(1)+size.get(1)-1, offset.get(2)+size.get(2)-1,offset.get(3)+size.get(3)-1,offset.get(4)+size.get(4)-1); if(step.get(0) < 1 || step.get(1) < 1 || step.get(2) < 1 || step.get(3) < 1 || step.get(4) < 1) { throw new DimensionsOutOfBoundsException( "Invalid step size: steps sizes must be 1 or greater"); } } }