From 87a7fbfcff9151b981187dc79a31d744a8e38b77 Mon Sep 17 00:00:00 2001 From: jbb01 <32650546+jbb01@users.noreply.github.com> Date: Sun, 4 Aug 2024 01:28:12 +0200 Subject: [PATCH] abstract Image --- .../render/BufferedImageCanvas.java | 39 +++++++++++++++++++ .../jonahbauer/raytracing/render/Camera.java | 26 ++++++++++--- .../jonahbauer/raytracing/render/Canvas.java | 21 ++++++++++ .../jonahbauer/raytracing/render/Image.java | 20 +++------- .../raytracing/render/ImageFormat.java | 14 +++---- 5 files changed, 93 insertions(+), 27 deletions(-) create mode 100644 src/main/java/eu/jonahbauer/raytracing/render/BufferedImageCanvas.java create mode 100644 src/main/java/eu/jonahbauer/raytracing/render/Canvas.java diff --git a/src/main/java/eu/jonahbauer/raytracing/render/BufferedImageCanvas.java b/src/main/java/eu/jonahbauer/raytracing/render/BufferedImageCanvas.java new file mode 100644 index 0000000..ebbfe0d --- /dev/null +++ b/src/main/java/eu/jonahbauer/raytracing/render/BufferedImageCanvas.java @@ -0,0 +1,39 @@ +package eu.jonahbauer.raytracing.render; + +import org.jetbrains.annotations.NotNull; + +import java.awt.image.BufferedImage; + +public class BufferedImageCanvas implements Canvas { + private final @NotNull BufferedImage image; + + public BufferedImageCanvas(int width, int height) { + this.image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + } + + public @NotNull BufferedImage getImage() { + return image; + } + + @Override + public int width() { + return image.getWidth(); + } + + @Override + public int height() { + return image.getHeight(); + } + + @Override + public @NotNull Color get(int x, int y) { + var rgb = image.getRGB(x, y); + return new Color((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); + } + + @Override + public void set(int x, int y, @NotNull Color color) { + var rgb = color.red() << 16 | color.green() << 8 | color.blue(); + image.setRGB(x, y, rgb); + } +} diff --git a/src/main/java/eu/jonahbauer/raytracing/render/Camera.java b/src/main/java/eu/jonahbauer/raytracing/render/Camera.java index f49c118..e839948 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/Camera.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/Camera.java @@ -8,6 +8,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.IntStream; public final class Camera { // image size @@ -72,8 +74,16 @@ public final class Camera { public @NotNull Image render(@NotNull Scene scene) { var image = new Image(width, height); + render(scene, image); + return image; + } - for (int y = 0; y < height; y++) { + public void render(@NotNull Scene scene, @NotNull Canvas canvas) { + if (canvas.width() != width || canvas.height() != height) throw new IllegalArgumentException(); + + var lines = new AtomicInteger(); + IntStream.range(0, height).parallel().forEach(y -> { + System.out.println(lines.incrementAndGet()); for (int x = 0; x < width; x++) { var r = 0d; var g = 0d; @@ -87,15 +97,13 @@ public final class Camera { b += color.b(); } - image.set(x, y, new Color( + canvas.set(x, y, new Color( Math.pow(r / samplesPerPixel, 1 / gamma), Math.pow(g / samplesPerPixel, 1 / gamma), Math.pow(b / samplesPerPixel, 1 / gamma) )); } - } - - return image; + }); } private @NotNull Ray getRay(int x, int y) { @@ -150,6 +158,14 @@ public final class Camera { return Color.lerp(Color.WHITE, Color.SKY, alt / Math.PI + 0.5); } + public int width() { + return width; + } + + public int height() { + return height; + } + public static class Builder { private int imageWidth = 1920; private int imageHeight = 1080; diff --git a/src/main/java/eu/jonahbauer/raytracing/render/Canvas.java b/src/main/java/eu/jonahbauer/raytracing/render/Canvas.java new file mode 100644 index 0000000..da39b51 --- /dev/null +++ b/src/main/java/eu/jonahbauer/raytracing/render/Canvas.java @@ -0,0 +1,21 @@ +package eu.jonahbauer.raytracing.render; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public interface Canvas { + int width(); + int height(); + + void set(int x, int y, @NotNull Color color); + @NotNull Color get(int x, int y); + + default @NotNull Stream pixels() { + return IntStream.range(0, height()) + .mapToObj(y -> IntStream.range(0, width()).mapToObj(x -> get(x, y))) + .flatMap(Function.identity()); + } +} diff --git a/src/main/java/eu/jonahbauer/raytracing/render/Image.java b/src/main/java/eu/jonahbauer/raytracing/render/Image.java index 703fb3a..c01eb02 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/Image.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/Image.java @@ -2,11 +2,9 @@ package eu.jonahbauer.raytracing.render; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; import java.util.Objects; -import java.util.stream.Stream; -public final class Image { +public final class Image implements Canvas { private final int width; private final int height; @@ -22,35 +20,27 @@ public final class Image { this.data = new Color[height][width]; } + @Override public int width() { return width; } + @Override public int height() { return height; } + @Override public @NotNull Color get(int x, int y) { Objects.checkIndex(x, width); Objects.checkIndex(y, height); return Objects.requireNonNullElse(this.data[y][x], Color.BLACK); } - public @NotNull Stream pixels() { - return Arrays.stream(data).flatMap(Arrays::stream).map(c -> Objects.requireNonNullElse(c, Color.BLACK)); - } - + @Override public void set(int x, int y, @NotNull Color color) { Objects.checkIndex(x, width); Objects.checkIndex(y, height); this.data[y][x] = Objects.requireNonNull(color); } - - public void set(int x, int y, int red, int green, int blue) { - set(x, y, new Color(red, green, blue)); - } - - public void set(int x, int y, double r, double g, double b) { - set(x, y, new Color(r, g, b)); - } } diff --git a/src/main/java/eu/jonahbauer/raytracing/render/ImageFormat.java b/src/main/java/eu/jonahbauer/raytracing/render/ImageFormat.java index 722b9c7..c49b936 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/ImageFormat.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/ImageFormat.java @@ -13,7 +13,7 @@ import java.util.zip.DeflaterOutputStream; public enum ImageFormat { PPM { @Override - public void write(@NotNull Image image, @NotNull OutputStream out) throws IOException { + public void write(@NotNull Canvas 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())); @@ -42,7 +42,7 @@ public enum ImageFormat { private static final int IEND_TYPE = 0x49454E44; @Override - public void write(@NotNull Image image, @NotNull OutputStream out) throws IOException { + public void write(@NotNull Canvas image, @NotNull OutputStream out) throws IOException { try (var data = new NoCloseDataOutputStream(out); var _ = data.closeable()) { data.write(MAGIC); @@ -52,7 +52,7 @@ public enum ImageFormat { } } - private void writeIHDR(@NotNull Image image, @NotNull DataOutputStream data) throws IOException { + private void writeIHDR(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException { data.writeInt(IHDR_LENGTH); try ( var crc = new CheckedOutputStream(data, new CRC32()); @@ -71,7 +71,7 @@ public enum ImageFormat { } } - private void writeIDAT(@NotNull Image image, @NotNull DataOutputStream data) throws IOException { + private void writeIDAT(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException { try ( var baos = new ByteArrayOutputStream(); var crc = new CheckedOutputStream(baos, new CRC32()); @@ -97,7 +97,7 @@ public enum ImageFormat { } } - private void writeIEND(@NotNull Image image, @NotNull DataOutputStream data) throws IOException { + private void writeIEND(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException { data.writeInt(0); data.writeInt(IEND_TYPE); data.writeInt(0); @@ -120,11 +120,11 @@ public enum ImageFormat { }, ; - public void write(@NotNull Image image, @NotNull Path path) throws IOException { + public void write(@NotNull Canvas 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; + public abstract void write(@NotNull Canvas image, @NotNull OutputStream out) throws IOException; }