/* * 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.IOException; import java.io.Serializable; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import loci.formats.CoreMetadata; import loci.formats.FormatException; import loci.formats.FormatTools; import loci.formats.IFormatReader; import ome.conditions.ResourceError; import ome.io.nio.DimensionsOutOfBoundsException; import ome.io.nio.PixelBuffer; import ome.io.nio.RomioPixelBuffer; import ome.util.PixelData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@link PixelBuffer} implementation which uses Bio-Formats to * read pixels data directly from original files. * * @since Beta4.1 */ public class BfPixelBuffer implements PixelBuffer, Serializable { private final static Logger log = LoggerFactory.getLogger(BfPixelBuffer.class); protected final String filePath; protected final IFormatReader bfReader; protected final AtomicReference<BfPixelsWrapper> reader = new AtomicReference<BfPixelsWrapper>(); private int seriesIndex = 0; /** * 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. */ public BfPixelBuffer(String filePath, IFormatReader bfReader) throws IOException, FormatException { this.filePath = filePath; this.bfReader = bfReader; } protected BfPixelsWrapper reader() { BfPixelsWrapper wrapper = reader.get(); if (wrapper == null) { try { // Note: the call to bfReader.setid inside the BfPixelsWrapper // ctor should be a no-op since the filePath is the same for // both calls. if (reader.compareAndSet(null, new BfPixelsWrapper(filePath, bfReader))) { wrapper = reader.get(); } } catch (FormatException fe) { log.debug("FormatException: " + filePath, fe); throw new ResourceError("FormatException: " + filePath + "\n" + fe.getMessage()); } catch (Exception e) { log.error("Failed to instantiate BfPixelsWrapper with " + filePath); throw new RuntimeException(e); } // Ensure that we're using the highest resolution level (100%) by // default. setSeries(seriesIndex); setResolutionLevel(getResolutionLevels() - 1); } return wrapper; } /** * Delegates to {@link IFormatReader#isLittleEndian()}. * @return See above. */ public boolean isLittleEndian() { // Ensure the reader has been initialized reader(); return bfReader.isLittleEndian(); } /** * Sets the current series in the underlying Bio-Formats reader. * @param series The series to set. */ public void setSeries(int series) { // Ensure the reader has been initialized reader(); bfReader.setSeries(series); seriesIndex = series; } /** * Retrieves the current series of the underlying Bio-Formats reader. * @return The series. */ public int getSeries() { // Ensure the reader has been initialized reader(); return bfReader.getSeries(); } public byte[] calculateMessageDigest() throws IOException { return reader().getMessageDigest(); } public void checkBounds(Integer x, Integer y, Integer z, Integer c, Integer t) throws DimensionsOutOfBoundsException { reader().checkBounds(x, y, z, c, t); } public void close() throws IOException { reader().close(); reader.set(null); } public int getByteWidth() { return reader().getByteWidth(); } public PixelData getCol(Integer x, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { final BfPixelsWrapper reader = reader(); PixelData d; byte[] buffer = new byte[reader.getColSize()]; reader.getCol(x,z,c,t,buffer); d = new PixelData(reader.getPixelsType(), ByteBuffer.wrap(buffer)); d.setOrder(isLittleEndian()? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); return d; } public byte[] getColDirect(Integer x, Integer z, Integer c, Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { try { final BfPixelsWrapper reader = reader(); reader.getCol(x,z,c,t,buffer); reader.swapIfRequired(buffer); } catch (FormatException e) { throw new RuntimeException(e); } return buffer; } public Integer getColSize() { return reader().getColSize(); } public long getId() { return reader().getId(); } public String getPath() { return reader().getPath(); } public PixelData getPlane(Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { final BfPixelsWrapper reader = reader(); PixelData d; int size = RomioPixelBuffer.safeLongToInteger(reader.getPlaneSize()); byte[] buffer = new byte[size]; reader.getPlane(z,c,t,buffer); d = new PixelData(reader.getPixelsType(), ByteBuffer.wrap(buffer)); d.setOrder(isLittleEndian()? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); return d; } public byte[] getPlaneDirect(Integer z, Integer c, Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { try { final BfPixelsWrapper reader = reader(); reader.getPlane(z,c,t,buffer); reader.swapIfRequired(buffer); } catch (FormatException e) { throw new RuntimeException(e); } return buffer; } public Long getPlaneOffset(Integer z, Integer c, Integer t) throws DimensionsOutOfBoundsException { return reader().getPlaneOffset(z,c,t); } public byte[] getPlaneRegionDirect(Integer z, Integer c, Integer t, Integer count, Integer offset, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { throw new UnsupportedOperationException( "Not yet supported, raise ticket to implement if required"); } public Long getPlaneSize() { return reader().getPlaneSize(); } public PixelData getRegion(Integer size, Long offset) throws IOException { throw new UnsupportedOperationException( "Not yet supported, raise ticket to implement if required"); } public byte[] getRegionDirect(Integer size, Long offset, byte[] buffer) throws IOException { throw new UnsupportedOperationException( "Not yet supported, raise ticket to implement if required"); } public PixelData getRow(Integer y, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { final BfPixelsWrapper reader = reader(); PixelData d; byte[] buffer = new byte[reader.getRowSize()]; reader.getRow(y,z,c,t,buffer); d = new PixelData(reader.getPixelsType(), ByteBuffer.wrap(buffer)); d.setOrder(isLittleEndian()? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); return d; } public byte[] getRowDirect(Integer y, Integer z, Integer c, Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { try { final BfPixelsWrapper reader = reader(); reader.getRow(y,z,c,t,buffer); reader.swapIfRequired(buffer); } catch (FormatException e) { throw new RuntimeException(e); } return buffer; } public Long getRowOffset(Integer y, Integer z, Integer c, Integer t) throws DimensionsOutOfBoundsException { return reader().getRowOffset(y,z,c,t); } public Integer getRowSize() { return reader().getRowSize(); } public int getSizeC() { return reader().getSizeC(); } public int getSizeT() { return reader().getSizeT(); } public int getSizeX() { return reader().getSizeX(); } public int getSizeY() { return reader().getSizeY(); } public int getSizeZ() { return reader().getSizeZ(); } public PixelData getStack(Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException { final BfPixelsWrapper reader = reader(); PixelData d; int size = RomioPixelBuffer.safeLongToInteger(reader.getStackSize()); byte[] buffer = new byte[size]; reader.getStack(c,t,buffer); d = new PixelData(reader.getPixelsType(), ByteBuffer.wrap(buffer)); d.setOrder(isLittleEndian()? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); return d; } public byte[] getStackDirect(Integer c, Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { try { final BfPixelsWrapper reader = reader(); reader.getStack(c,t,buffer); reader.swapIfRequired(buffer); } catch (FormatException e) { throw new RuntimeException(e); } return buffer; } public Long getStackOffset(Integer c, Integer t) throws DimensionsOutOfBoundsException { return reader().getStackOffset(c,t); } public Long getStackSize() { return reader().getStackSize(); } public PixelData getTimepoint(Integer t) throws IOException, DimensionsOutOfBoundsException { final BfPixelsWrapper reader = reader(); PixelData d; int size = RomioPixelBuffer.safeLongToInteger( reader.getTimepointSize()); byte[] buffer = new byte[size]; reader.getTimepoint(t,buffer); d = new PixelData(reader.getPixelsType(), ByteBuffer.wrap(buffer)); d.setOrder(isLittleEndian()? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); return d; } public byte[] getTimepointDirect(Integer t, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { try { BfPixelsWrapper reader = reader(); reader.getTimepoint(t,buffer); reader.swapIfRequired(buffer); } catch (FormatException e) { throw new RuntimeException(e); } return buffer; } public Long getTimepointOffset(Integer t) throws DimensionsOutOfBoundsException { return reader().getTimepointOffset(t); } public Long getTimepointSize() { return reader().getTimepointSize(); } public Long getTotalSize() { return reader().getTotalSize(); } public boolean isFloat() { return reader().isFloat(); } public boolean isSigned() { return reader().isSigned(); } public void setPlane(ByteBuffer buffer, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException("Cannot write to repository"); } public void setPlane(byte[] buffer, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException("Cannot write to repository"); } public void setRegion(Integer size, Long offset, byte[] buffer) throws IOException, BufferOverflowException { throw new UnsupportedOperationException("Cannot write to repository"); } public void setRegion(Integer size, Long offset, ByteBuffer buffer) throws IOException, BufferOverflowException { throw new UnsupportedOperationException("Cannot write to repository"); } public void setRow(ByteBuffer buffer, Integer y, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException("Cannot write to repository"); } public void setStack(ByteBuffer buffer, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException("Cannot write to repository"); } public void setStack(byte[] buffer, Integer z, Integer c, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException("Cannot write to repository"); } public void setTimepoint(ByteBuffer buffer, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException("Cannot write to repository"); } public void setTimepoint(byte[] buffer, Integer t) throws IOException, DimensionsOutOfBoundsException, BufferOverflowException { throw new UnsupportedOperationException("Cannot write to repository"); } public Long getHypercubeSize(List<Integer> offset, List<Integer> size, List<Integer> step) throws DimensionsOutOfBoundsException { final BfPixelsWrapper reader = reader(); return reader.getHypercubeSize(offset,size,step); } public PixelData getHypercube(List<Integer> offset, List<Integer> size, List<Integer> step) throws IOException, DimensionsOutOfBoundsException { final BfPixelsWrapper reader = reader(); PixelData d; int hypercubeSize = RomioPixelBuffer.safeLongToInteger( getHypercubeSize(offset,size,step)); byte[] buffer = new byte[hypercubeSize]; reader.getHypercube(offset,size,step,buffer); d = new PixelData(reader.getPixelsType(), ByteBuffer.wrap(buffer)); d.setOrder(isLittleEndian()? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); return d; } public byte[] getHypercubeDirect(List<Integer> offset, List<Integer> size, List<Integer> step, byte[] buffer) throws IOException, DimensionsOutOfBoundsException { try { final BfPixelsWrapper reader = reader(); reader.getHypercube(offset,size,step,buffer); reader.swapIfRequired(buffer); } catch (FormatException e) { throw new RuntimeException(e); } return buffer; } public PixelData getPlaneRegion(Integer x, Integer y, Integer width, Integer height, Integer z, Integer c, Integer t, Integer stride) throws IOException, DimensionsOutOfBoundsException { List<Integer> offset = Arrays.asList(new Integer[]{x,y,z,c,t}); List<Integer> size = Arrays.asList(new Integer[]{width,height,1,1,1}); List<Integer> step = Arrays.asList(new Integer[]{stride+1,stride+1,1,1,1}); return getHypercube(offset, size, step); } /* (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 { final BfPixelsWrapper reader = reader(); byte[] buffer = new byte[ w * h * FormatTools.getBytesPerPixel(reader.getPixelsType())]; try { // Call getTile on reader() rather than on this // so as not to swap the bytes twice. reader().getTile(z, c, t, x, y, w, h, buffer); PixelData d = new PixelData( reader.getPixelsType(), ByteBuffer.wrap(buffer)); d.setOrder(isLittleEndian()? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); return d; } catch (FormatException e) { throw new RuntimeException(e); } } /* (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 { try { reader().getTile(z, c, t, x, y, w, h, buffer); reader().swapIfRequired(buffer); return buffer; } catch (FormatException e) { throw new RuntimeException(e); } } /* (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 { throw new UnsupportedOperationException("Cannot write to repository"); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getResolutionLevel() */ public int getResolutionLevel() { // Ensure the reader has been initialized reader(); // The highest resolution level (100%) is actually the first series return Math.abs( bfReader.getResolution() - (getResolutionLevels() - 1)); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getResolutionLevels() */ public int getResolutionLevels() { // Ensure the reader has been initialized reader(); return bfReader.getResolutionCount(); } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#getTileSize() */ public Dimension getTileSize() { // Ensure the reader has been initialized reader(); return new Dimension(bfReader.getOptimalTileWidth(), bfReader.getOptimalTileHeight()); } public List<List<Integer>> getResolutionDescriptions() { final List<List<Integer>> rv = new ArrayList<List<Integer>>(); final int no = bfReader.getResolutionCount(); final List<CoreMetadata> cms = bfReader.getCoreMetadataList(); for (int i = 0; i < no; i++) { int coreIndex = bfReader.seriesToCoreIndex(bfReader.getSeries()) + i; CoreMetadata cm = cms.get(coreIndex); List<Integer> sizes = Arrays.asList(cm.sizeX, cm.sizeY); rv.add(sizes); } return rv; } /* (non-Javadoc) * @see ome.io.nio.PixelBuffer#setResolutionLevel(int) */ public void setResolutionLevel(int resolutionLevel) { // Ensure the reader has been initialized reader(); // The highest resolution level (100%) is actually the first series bfReader.setResolution(Math.abs( resolutionLevel - (getResolutionLevels() - 1))); } }