diff --git a/java/pom.xml b/java/pom.xml index 5ca09ae..026ba39 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.micro-manager.ndtiffstorage NDTiffStorage - 2.19.2 + 2.19.3 jar NDTiff Storage file format Java-based writer and reader used for NDTiffStorage format and NDRAM storage diff --git a/java/src/main/java/org/micromanager/ndtiffstorage/NDTiffWriter.java b/java/src/main/java/org/micromanager/ndtiffstorage/NDTiffWriter.java index 4427c58..5996891 100644 --- a/java/src/main/java/org/micromanager/ndtiffstorage/NDTiffWriter.java +++ b/java/src/main/java/org/micromanager/ndtiffstorage/NDTiffWriter.java @@ -292,9 +292,48 @@ private long unsignInt(int i) { public void overwritePixels(String indexKey, Object pixels, boolean rgb) throws IOException { long pixelOffset = indexMap_.get(indexKey).pixOffset_; Buffer pixBuff = getPixelBuffer(pixels, rgb); + if (fileChannel_ == null) { + // This writer has already been finishedWriting() (its file was rotated/closed as + // the file set grew). Pixels still need to be overwritten in place -- e.g. when + // building low-res pyramid levels, a downsampled tile receives contributions from + // several full-res tiles, and the tile's first write may live in an earlier, + // now-closed file. Reopen the file read-write just for this in-place write, then + // close it again so we don't leak file descriptors for every rotated file. + overwritePixelsReopening(pixBuff, pixelOffset); + return; + } fileChannelWrite(pixBuff, pixelOffset); } + /** + * Overwrite pixels in place in a file whose channel has been closed (after + * finishedWriting()). Opens the existing file read-write at {@code position}, writes the + * buffer, and closes -- the writer stays in its finished state. The index entry's + * pixOffset_ is an absolute file offset, so no header re-parsing is needed. + */ + private void overwritePixelsReopening(final Buffer buffer, final long position) + throws IOException { + buffer.rewind(); + ByteBuffer byteBuffer = (ByteBuffer) buffer; + RandomAccessFile raf = null; + try { + raf = new RandomAccessFile(filename_, "rw"); + FileChannel ch = raf.getChannel(); + // A single positional write is not guaranteed to write the whole buffer; loop + // until it is fully written, advancing the file position by the bytes written. + long pos = position; + while (byteBuffer.hasRemaining()) { + pos += ch.write(byteBuffer, pos); + } + } finally { + // Recycle in finally so a write/IO failure cannot leak a large direct buffer. + masterMPTiffStorage_.tryRecycleLargeBuffer(byteBuffer); + if (raf != null) { + raf.close(); + } + } + } + private IndexEntryData writeIFD(String indexKey, Object pixels, byte[] metadata, boolean rgb, int imageHeight, int imageWidth, int bitDepth ) throws IOException {