Skip to content

Commit

Permalink
add fallback for non-FileOutputStreams
Browse files Browse the repository at this point in the history
  • Loading branch information
Maximilian Stiede committed Nov 1, 2023
1 parent 35ba555 commit be4ad1f
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

import se.llbit.chunky.renderer.scene.AlphaBuffer;
import se.llbit.chunky.renderer.scene.Scene;
Expand Down Expand Up @@ -59,15 +65,37 @@ public boolean wantsPostprocessing() {

@Override
public void write(OutputStream out, Scene scene, TaskTracker taskTracker) throws IOException {
assert(out instanceof FileOutputStream);
try (
TaskTracker.Task task = taskTracker.task("Writing TIFF");
TiffFileWriter writer = new TiffFileWriter(
((FileOutputStream) out).getChannel(),
CompressionType.DEFLATE
)
) {
writer.export(scene, task);
try (TaskTracker.Task task = taskTracker.task("Writing TIFF")) {
if (out instanceof FileOutputStream) {
write(((FileOutputStream) out).getChannel(), scene, task);
} else {
// fallback for the case, that the output stream was not created on a file
Path tempFile = Files.createTempFile(scene.name + "-", getExtension());
try (FileChannel fileChannel = FileChannel.open(tempFile, StandardOpenOption.DELETE_ON_CLOSE,
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.READ, StandardOpenOption.WRITE
)) {
write(fileChannel, scene, task);
// rewind channel
fileChannel.position(0);
try (InputStream inputStream = Channels.newInputStream(fileChannel)) {
// copy temp file to output
inputStream.transferTo(out);
}
}
}
}
}

/**
* Note: does not (!) close the file channel after writing
*/
private void write(FileChannel fileChannel, Scene scene, TaskTracker.Task task) throws IOException {
TiffFileWriter writer = new TiffFileWriter(
fileChannel,
CompressionType.DEFLATE
);
writer.export(scene, task);
writer.doFinalization();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,22 @@ private <T extends UnfinalizedData<?>> T writeUnfinalized(T ud, int byteCount) t
return ud;
}

@Override
public void close() throws IOException {
/**
* writes remaining unfinalized data
*/
public void doFinalization() throws IOException {
for(UnfinalizedData<?> data : finalizationQueue) {
data.write(this);
}
finalizationQueue.clear();
}

/**
* does finalization, then closes the output stream
*/
@Override
public void close() throws IOException {
doFinalization();
super.close();
}

Expand Down
13 changes: 10 additions & 3 deletions chunky/src/java/se/llbit/imageformats/tiff/TiffFileWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class TiffFileWriter implements AutoCloseable {

private final FinalizableBFCOutputStream out;
private final CompressionType compressionType;
private FinalizableBFCOutputStream.UnfinalizedData.Int nextIFDOffset;

public TiffFileWriter(
FileChannel fileChannel,
Expand All @@ -54,12 +55,17 @@ public TiffFileWriter(
// - MM -> magic bytes
// - \0* -> magic number 42 for big-endian byte order
out.writeInt(0x4D4D002A);
nextIFDOffset = out.writeUnfinalizedInt();
}

public TiffFileWriter(FileOutputStream outputStream) throws IOException {
this(outputStream.getChannel(), CompressionType.NONE);
}

public void doFinalization() throws IOException {
out.doFinalization();
}

@Override
public void close() throws IOException {
out.close();
Expand All @@ -68,11 +74,12 @@ public void close() throws IOException {
/**
* Export sample buffer as Baseline TIFF RGB image / TIFF Class R image
* with 32 bits per color component.
*
* <p>Note: This method does not close the output stream, and can be called multiple times for multiple layers.
* Use {@link #doFinalization()} to complete the export.
*/
public void export(Scene scene, TaskTracker.Task task) throws IOException {
FinalizableBFCOutputStream.UnfinalizedData.Int ifdOffset = out.writeUnfinalizedInt();

writePrimaryIDF(ifdOffset, scene, task);
nextIFDOffset = writePrimaryIDF(nextIFDOffset, scene, task);
}

private static final DateTimeFormatter DATETIME_FORMAT = DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss");
Expand Down

0 comments on commit be4ad1f

Please sign in to comment.