-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* move IO utils into their own package * refactor TIFF writing to be more performant and easier to extend in the future, add BufferedFileChannelOutputStream with some useful file writing enhancements * add fallback for non-FileOutputStreams
- Loading branch information
Maximilian Stiede
authored
Nov 1, 2023
1 parent
24eda0e
commit 990bdc5
Showing
18 changed files
with
849 additions
and
287 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
chunky/src/java/se/llbit/chunky/renderer/renderdump/GzipDumpFormat.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
chunky/src/java/se/llbit/chunky/renderer/renderdump/HuffmanDumpFormat.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package se.llbit.imageformats.tiff; | ||
|
||
import java.io.IOException; | ||
|
||
public class BasicIFD extends ImageFileDirectory { | ||
final CompressionType compressionType; | ||
|
||
public BasicIFD( | ||
int width, int height, | ||
CompressionType compressionType | ||
) throws IOException { | ||
this.compressionType = compressionType; | ||
|
||
// RGB full color | ||
addTag(IFDTag.TAG_PHOTOMETRIC_INTERPRETATION, (short) 2); | ||
// Store pixel components contiguously [RGBRGBRGB...] | ||
addTag(IFDTag.TAG_PLANAR_CONFIGURATION, (short) 1); | ||
|
||
assert (width <= Short.MAX_VALUE); | ||
addTag(IFDTag.TAG_IMAGE_WIDTH, (short) width); | ||
assert (height <= Short.MAX_VALUE); | ||
addTag(IFDTag.TAG_IMAGE_HEIGHT, (short) height); | ||
// The 0th row represents the visual top of the image, and the 0th column represents the visual left-hand side. | ||
addTag(IFDTag.TAG_ORIENTATION, (short) 1); | ||
|
||
// No compression, but pack data into bytes as tightly as possible, leaving no unused | ||
// bits (except at the end of a row). The component values are stored as an array of | ||
// type BYTE. Each scan line (row) is padded to the next BYTE boundary. | ||
addTag(IFDTag.TAG_COMPRESSION_TYPE, compressionType.id); | ||
|
||
// Image does not have a physical size | ||
addTag(IFDTag.TAG_RESOLUTION_UNIT, (short) 1); // not an absolute unit | ||
addMultiTag(IFDTag.TAG_X_RESOLUTION, new int[]{1, 1}); | ||
addMultiTag(IFDTag.TAG_Y_RESOLUTION, new int[]{1, 1}); | ||
|
||
// "Compressed or uncompressed image data can be stored almost anywhere in a | ||
// TIFF file. TIFF also supports breaking an image into separate strips for increased | ||
// editing flexibility and efficient I/O buffering." | ||
// We will use exactly 1 strip, therefore the relevant tags have only 1 entry with all rows in 1 strip. | ||
addTag(IFDTag.TAG_ROWS_PER_STRIP, height); | ||
} | ||
|
||
@Override | ||
void writePixelData( | ||
FinalizableBFCOutputStream out, | ||
PixelDataWriter writer | ||
) throws IOException { | ||
compressionType.writePixelData(out, writer); | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
chunky/src/java/se/llbit/imageformats/tiff/CompressionType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package se.llbit.imageformats.tiff; | ||
|
||
import java.io.DataOutputStream; | ||
import java.io.IOException; | ||
import java.util.zip.Deflater; | ||
import java.util.zip.DeflaterOutputStream; | ||
|
||
public enum CompressionType { | ||
NONE(0x0001), | ||
DEFLATE(0x80B2); | ||
|
||
final short id; | ||
|
||
CompressionType(int id) { | ||
this.id = (short) id; | ||
} | ||
|
||
void writePixelData( | ||
FinalizableBFCOutputStream out, | ||
ImageFileDirectory.PixelDataWriter writer | ||
) throws IOException { | ||
switch (this) { | ||
case NONE: | ||
writer.writePixelData(out); | ||
out.flush(); | ||
break; | ||
|
||
case DEFLATE: | ||
Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION, false); | ||
DeflaterOutputStream deflOut = new DeflaterOutputStream(out, deflater, 16 * 1024, true); | ||
writer.writePixelData(new DataOutputStream(deflOut)); | ||
deflOut.finish(); | ||
deflater.end(); | ||
break; | ||
} | ||
} | ||
} |
87 changes: 87 additions & 0 deletions
87
chunky/src/java/se/llbit/imageformats/tiff/FinalizableBFCOutputStream.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package se.llbit.imageformats.tiff; | ||
|
||
import se.llbit.util.io.BufferedFileChannelOutputStream; | ||
|
||
import java.io.IOException; | ||
import java.nio.channels.FileChannel; | ||
import java.util.ArrayDeque; | ||
import java.util.Deque; | ||
|
||
class FinalizableBFCOutputStream extends BufferedFileChannelOutputStream { | ||
|
||
private static final Deque<UnfinalizedData<?>> finalizationQueue = new ArrayDeque<>(); | ||
|
||
public FinalizableBFCOutputStream(FileChannel fileChannel) { | ||
super(fileChannel); | ||
} | ||
|
||
void ensureAlignment() throws IOException { | ||
if ((position() & 0b1) != 0) | ||
write((byte) 0); | ||
} | ||
|
||
UnfinalizedData.Int writeUnfinalizedInt() throws IOException { | ||
return writeUnfinalized(new UnfinalizedData.Int((int) position()), 4); | ||
} | ||
private <T extends UnfinalizedData<?>> T writeUnfinalized(T ud, int byteCount) throws IOException { | ||
finalizationQueue.add(ud); | ||
skip(byteCount); | ||
return ud; | ||
} | ||
|
||
/** | ||
* 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(); | ||
} | ||
|
||
static abstract class UnfinalizedData<T> { | ||
final long position; | ||
protected T data; | ||
|
||
UnfinalizedData(long position) { | ||
this.position = position; | ||
} | ||
|
||
public void setData(T data) { | ||
this.data = data; | ||
} | ||
|
||
public T getData() { | ||
return data; | ||
} | ||
|
||
public void write(FinalizableBFCOutputStream out) throws IOException { | ||
out.position(position); | ||
if(data != null) { | ||
writeData(out); | ||
} | ||
} | ||
|
||
abstract void writeData(FinalizableBFCOutputStream out) throws IOException; | ||
|
||
static class Int extends UnfinalizedData<Integer> { | ||
Int(long position) { | ||
super(position); | ||
} | ||
|
||
@Override | ||
void writeData(FinalizableBFCOutputStream out) throws IOException { | ||
out.writeInt(data); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.