add support for png images

main
jbb01 6 months ago
parent cf0ed6f13f
commit 927f63adf0

@ -1,7 +1,7 @@
package eu.jonahbauer.raytracing;
import eu.jonahbauer.raytracing.render.Camera;
import eu.jonahbauer.raytracing.render.ImageIO;
import eu.jonahbauer.raytracing.render.ImageFormat;
import eu.jonahbauer.raytracing.scene.Scene;
import eu.jonahbauer.raytracing.scene.Sphere;
@ -17,6 +17,6 @@ public class Main {
var camera = new Camera(512, 2, 16 / 9d);
var image = camera.render(scene);
ImageIO.write(image, Path.of("scene-" + System.currentTimeMillis() + ".ppm"));
ImageFormat.PNG.write(image, Path.of("scene-" + System.currentTimeMillis() + ".png"));
}
}

@ -0,0 +1,130 @@
package eu.jonahbauer.raytracing.render;
import org.jetbrains.annotations.NotNull;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.DeflaterOutputStream;
public enum ImageFormat {
PPM {
@Override
public void write(@NotNull Image image, @NotNull OutputStream out) throws IOException {
try (var writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.US_ASCII))) {
writer.write("P3\n");
writer.write(String.valueOf(image.width()));
writer.write(" ");
writer.write(String.valueOf(image.height()));
writer.write("\n255\n");
var it = image.pixels().iterator();
while (it.hasNext()) {
var color = it.next();
writer.write(String.valueOf(color.red()));
writer.write(" ");
writer.write(String.valueOf(color.green()));
writer.write(" ");
writer.write(String.valueOf(color.blue()));
writer.write("\n");
}
}
}
},
PNG {
private static final byte[] MAGIC = new byte[] { (byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
private static final int IHDR_LENGTH = 13;
private static final int IHDR_TYPE = 0x49484452;
private static final int IDAT_TYPE = 0x49444154;
private static final int IEND_TYPE = 0x49454E44;
@Override
public void write(@NotNull Image image, @NotNull OutputStream out) throws IOException {
try (var data = new NoCloseDataOutputStream(out); var _ = data.closeable()) {
data.write(MAGIC);
writeIHDR(image, data);
writeIDAT(image, data);
writeIEND(image, data);
}
}
private void writeIHDR(@NotNull Image image, @NotNull DataOutputStream data) throws IOException {
data.writeInt(IHDR_LENGTH);
try (
var crc = new CheckedOutputStream(data, new CRC32());
var ihdr = new DataOutputStream(crc)
) {
ihdr.writeInt(IHDR_TYPE);
ihdr.writeInt(image.width()); // image width
ihdr.writeInt(image.height()); // image height
ihdr.writeByte(8); // bit depth
ihdr.writeByte(2); // color type
ihdr.writeByte(0); // compression method
ihdr.writeByte(0); // filter method
ihdr.writeByte(0); // interlace method
ihdr.flush();
data.writeInt((int) crc.getChecksum().getValue());
}
}
private void writeIDAT(@NotNull Image image, @NotNull DataOutputStream data) throws IOException {
try (
var baos = new ByteArrayOutputStream();
var crc = new CheckedOutputStream(baos, new CRC32());
var idat = new DataOutputStream(crc)
) {
idat.writeInt(IDAT_TYPE);
try (var deflate = new DataOutputStream(new DeflaterOutputStream(idat))) {
var pixels = image.pixels().iterator();
for (int i = 0; pixels.hasNext(); i = (i + 1) % image.width()) {
if (i == 0) deflate.writeByte(0); // filter type
var pixel = pixels.next();
deflate.writeByte(pixel.red());
deflate.writeByte(pixel.green());
deflate.writeByte(pixel.blue());
}
}
var bytes = baos.toByteArray();
data.writeInt(bytes.length);
data.write(bytes);
data.writeInt((int) crc.getChecksum().getValue());
}
}
private void writeIEND(@NotNull Image image, @NotNull DataOutputStream data) throws IOException {
data.writeInt(0);
data.writeInt(IEND_TYPE);
data.writeInt(0);
}
private static class NoCloseDataOutputStream extends DataOutputStream {
public NoCloseDataOutputStream(OutputStream out) {
super(out);
}
@Override
public void close() {
// do nothing
}
public Closeable closeable() {
return super::close;
}
}
},
;
public void write(@NotNull Image image, @NotNull Path path) throws IOException {
try (var out = Files.newOutputStream(path)) {
write(image, out);
}
}
public abstract void write(@NotNull Image image, @NotNull OutputStream out) throws IOException;
}

@ -1,34 +0,0 @@
package eu.jonahbauer.raytracing.render;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public final class ImageIO {
private ImageIO() {
throw new UnsupportedOperationException();
}
public static void write(@NotNull Image image, @NotNull Path path) throws IOException {
try (var out = Files.newBufferedWriter(path)) {
out.write("P3\n");
out.write(String.valueOf(image.width()));
out.write(" ");
out.write(String.valueOf(image.height()));
out.write("\n255\n");
var it = image.pixels().iterator();
while (it.hasNext()) {
var color = it.next();
out.write(String.valueOf(color.red()));
out.write(" ");
out.write(String.valueOf(color.green()));
out.write(" ");
out.write(String.valueOf(color.blue()));
out.write("\n");
}
}
}
}
Loading…
Cancel
Save