I am running a Javalin web server and want to create an algorithmic image with JavaFX Canvas and serve that over http. So far I have created a new Canvas and embedded it inside a Stage and it works fine. But when I try to use the Canvas standalone from the webserver thread as follows:
public byte[] getResponse() throws IOException {
WritableImage snapshot = new FlowersFX(width, height).draw().getCanvas().snapshot(null, null);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", stream);
return stream.toByteArray();
}
I get java.lang.IllegalStateException:
java.lang.IllegalStateException: Not on FX application thread; currentThread = JettyServerThreadPool-33
at [email protected]/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:294)
at [email protected]/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:481)
at [email protected]/javafx.scene.Node.snapshot(Node.java:2255)
at StaticImager/top.bagadbilla.handler.FlowerGeneratorHandler.getResponse(FlowerGeneratorHandler.java:35)
at StaticImager/top.bagadbilla.App.lambda$run$8(App.java:55)
I have tried running Platform.startup() before running the server and then creating and updating the canvas inside Platform.runLater() but the thread just freezes indefinitely, and I dont like having to run another thread when a separate thread is created by the webserver anyway.
EDIT: Here is the code when using Platform.runLater to run the canvas in application fx thread and it keeps on waiting for future.get():
public class FlowerGeneratorHandler {
private Canvas canvas;
private final RunnableFuture<Void> future;
public FlowerGeneratorHandler(int width, int height) {
Runnable runnable = () -> canvas = new FlowersFX(width, height).draw().getCanvas();
future = new FutureTask<>(runnable, null);
Platform.runLater(future);
}
public byte[] getResponse() throws IOException, ExecutionException, InterruptedException {
future.get();
WritableImage snapshot = canvas.snapshot(null, null);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", stream);
return stream.toByteArray();
}
}
and Platform is started before:
Javalin.create()
.events(eventConfig -> {
eventConfig.serverStarting(() -> Platform.startup(() -> {
System.out.println("Starting platform");
}));
eventConfig.serverStopping(Platform::exit);
})
.....
EDIT2: Changed future being passed on as Runnable to Platform.runLater(), but the original exception is back java.lang.IllegalStateException: Not on FX application thread; currentThread = JettyServerThreadPool-33
EDIT3: turns out canvas.snapshot() call needs to be on the FX Application thread. Working fine after moving it inside runnable as well.