From ed75ee147548cb34de459b1fd3b231687edba898 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Tue, 8 Apr 2025 16:34:38 +0200 Subject: [PATCH 1/2] Add basic real time rendering example --- .../io/computenode/cyfra/ImageUtility.scala | 16 ++++- .../cyfra/samples/RealTimeRendering.scala | 61 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 src/main/scala/io/computenode/cyfra/samples/RealTimeRendering.scala diff --git a/src/main/scala/io/computenode/cyfra/ImageUtility.scala b/src/main/scala/io/computenode/cyfra/ImageUtility.scala index 36ce6aae..d2771913 100644 --- a/src/main/scala/io/computenode/cyfra/ImageUtility.scala +++ b/src/main/scala/io/computenode/cyfra/ImageUtility.scala @@ -4,10 +4,13 @@ import java.awt.image.BufferedImage import java.io.File import java.nio.file.Path import javax.imageio.ImageIO +import javax.swing.JFrame +import javax.swing.JLabel +import javax.swing.ImageIcon object ImageUtility { def renderToImage(arr: Array[(Float, Float, Float, Float)], n: Int, location: Path): Unit = renderToImage(arr, n, n, location) - def renderToImage(arr: Array[(Float, Float, Float, Float)], w: Int, h: Int, location: Path): Unit = { + def renderBufferedImg(arr: Array[(Float, Float, Float, Float)], w: Int, h: Int): BufferedImage = { val image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB) for (y <- 0 until h) { for (x <- 0 until w) { @@ -17,9 +20,20 @@ object ImageUtility { image.setRGB(x, y, (iR << 16) | (iG << 8) | iB) } } + image + } + def renderToImage(arr: Array[(Float, Float, Float, Float)], w: Int, h: Int, location: Path): Unit = { + val image = renderBufferedImg(arr, w, h) val outputFile = location.toFile ImageIO.write(image, "png", outputFile) } + def displayImageToWindow(image: BufferedImage, frame: JFrame): Unit = { + frame.getContentPane.removeAll() + frame.getContentPane.add(new JLabel(new ImageIcon(image))) + frame.pack() + frame.repaint() + } + } diff --git a/src/main/scala/io/computenode/cyfra/samples/RealTimeRendering.scala b/src/main/scala/io/computenode/cyfra/samples/RealTimeRendering.scala new file mode 100644 index 00000000..300adcdc --- /dev/null +++ b/src/main/scala/io/computenode/cyfra/samples/RealTimeRendering.scala @@ -0,0 +1,61 @@ +package io.computenode.cyfra.samples.slides + +import io.computenode.cyfra.dsl.Value.{Float32, Int32, Vec4} +import io.computenode.cyfra.dsl.Expression.* +import io.computenode.cyfra.dsl.Value.* +import io.computenode.cyfra.dsl.* + +import java.awt.image.BufferedImage +import java.io.File +import javax.imageio.ImageIO +import scala.concurrent.ExecutionContext.Implicits +import scala.concurrent.{Await, ExecutionContext} +import io.computenode.cyfra.dsl.Algebra.* +import io.computenode.cyfra.dsl.Algebra.given +import io.computenode.cyfra.dsl.given + +import scala.concurrent.duration.DurationInt +import io.computenode.cyfra.dsl.Functions.* +import io.computenode.cyfra.dsl.Control.* +import io.computenode.cyfra.dsl.{Empty, GArray2DFunction, GSeq, GStruct, Vec4FloatMem} +import io.computenode.cyfra.{ImageUtility} + +import javax.swing.JFrame +import java.awt.event.KeyAdapter +import java.awt.event.KeyEvent + +@main +def realTimeRendering = + val dim = 1024 + val frame = new JFrame("Cyfra Live Raytracing") + frame.setVisible(true) + + + def computeImage(xLimit : Int): GArray2DFunction[Empty, Vec4[Float32], Vec4[Float32]] = GArray2DFunction(dim, dim, { + case (_, (x: Int32, _), _) => + when ((x < xLimit)){ + (1f, 1f, 1f, 1f) + } otherwise{ + (0f, 0f, 0f, 1f) + } + }) + + var xLimit = 0 + frame.addKeyListener(new KeyAdapter { + override def keyPressed(e: KeyEvent): Unit = { + e.getKeyCode match { + case KeyEvent.VK_LEFT => xLimit = math.max(0, xLimit - 1) + case KeyEvent.VK_RIGHT => xLimit = math.min(dim, xLimit + 1) + case _ => () + } + } + }) + + while (true) { + val mem = Vec4FloatMem(Array.fill(dim * dim)((0f,0f,0f,0f))) + val result = Await.result(mem.map(computeImage(xLimit)), 1.second) + + val image = ImageUtility.renderBufferedImg(result, dim, dim) + ImageUtility.displayImageToWindow(image, frame) + Thread.sleep(20) + } \ No newline at end of file From 1692671e03b578682e64e97bdd141b1989b71cec Mon Sep 17 00:00:00 2001 From: Matthieu Date: Wed, 9 Apr 2025 14:49:58 +0200 Subject: [PATCH 2/2] Improve performance of the simple real time rendering implementation --- .../scala/io/computenode/cyfra/ImageUtility.scala | 8 ++++---- .../computenode/cyfra/samples/RealTimeRendering.scala | 11 +++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/scala/io/computenode/cyfra/ImageUtility.scala b/src/main/scala/io/computenode/cyfra/ImageUtility.scala index d2771913..49dca7fd 100644 --- a/src/main/scala/io/computenode/cyfra/ImageUtility.scala +++ b/src/main/scala/io/computenode/cyfra/ImageUtility.scala @@ -7,6 +7,7 @@ import javax.imageio.ImageIO import javax.swing.JFrame import javax.swing.JLabel import javax.swing.ImageIcon +import java.awt.Component object ImageUtility { def renderToImage(arr: Array[(Float, Float, Float, Float)], n: Int, location: Path): Unit = renderToImage(arr, n, n, location) @@ -30,10 +31,9 @@ object ImageUtility { } def displayImageToWindow(image: BufferedImage, frame: JFrame): Unit = { - frame.getContentPane.removeAll() - frame.getContentPane.add(new JLabel(new ImageIcon(image))) - frame.pack() + frame.getContentPane.getComponents.collectFirst { case label: JLabel => + label.setIcon(new ImageIcon(image)) + } frame.repaint() } - } diff --git a/src/main/scala/io/computenode/cyfra/samples/RealTimeRendering.scala b/src/main/scala/io/computenode/cyfra/samples/RealTimeRendering.scala index 300adcdc..46394c66 100644 --- a/src/main/scala/io/computenode/cyfra/samples/RealTimeRendering.scala +++ b/src/main/scala/io/computenode/cyfra/samples/RealTimeRendering.scala @@ -23,14 +23,23 @@ import io.computenode.cyfra.{ImageUtility} import javax.swing.JFrame import java.awt.event.KeyAdapter import java.awt.event.KeyEvent +import javax.swing.JLabel +import java.awt.Color +import javax.swing.ImageIcon @main def realTimeRendering = + val dim = 1024 + val emptyImage = new BufferedImage(dim, dim, BufferedImage.TYPE_INT_RGB) val frame = new JFrame("Cyfra Live Raytracing") + frame.getContentPane.add(new JLabel(new ImageIcon(emptyImage))) + frame.pack() frame.setVisible(true) + + def computeImage(xLimit : Int): GArray2DFunction[Empty, Vec4[Float32], Vec4[Float32]] = GArray2DFunction(dim, dim, { case (_, (x: Int32, _), _) => when ((x < xLimit)){ @@ -54,8 +63,6 @@ def realTimeRendering = while (true) { val mem = Vec4FloatMem(Array.fill(dim * dim)((0f,0f,0f,0f))) val result = Await.result(mem.map(computeImage(xLimit)), 1.second) - val image = ImageUtility.renderBufferedImg(result, dim, dim) ImageUtility.displayImageToWindow(image, frame) - Thread.sleep(20) } \ No newline at end of file