2

I'm researching if it makes sense to pass an application from Swing to JavaFx 8 and I need help. The application visualises images real time, basically I update the image with a certain frequency, for example 1 second. The dimension of the images is 28k x 4k; When I use JavaFx imageView the performance is bad compared to Swing.(The fps are low, around 1-2, the application is lagging). I do it in the following way:

    animation = new Timeline();
    animation.getKeyFrames().add(new KeyFrame(Duration.millis(1000), new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent actionEvent) {
            for (int count = 0; count < 1; count++) {
                if (indicator)
                    imageView.setImage(writableImage);
                else
                    imageView.setImage(image1);
                indicator = !indicator;
            }
        }
    }));
    animation.setCycleCount(Animation.INDEFINITE);

So I tried to use the JavaFx canvas :

    imageCanvas.setHeight(image1.getHeight());
    imageCanvas.setWidth(image1.getWidth());
    imageCanvas.getGraphicsContext2D().drawImage(image1, 0, 0);

When I try it, I get the following error.

java.lang.NullPointerException
    at com.sun.prism.impl.BaseGraphics.drawTexture(BaseGraphics.java:400)
    at com.sun.prism.impl.ps.BaseShaderGraphics.drawTexture(BaseShaderGraphics.java:139)
    at com.sun.javafx.sg.prism.NGCanvas.handleRenderOp(NGCanvas.java:1336)
    at com.sun.javafx.sg.prism.NGCanvas.renderStream(NGCanvas.java:1086)
    at com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:595)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2067)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1959)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:576)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2067)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1959)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:576)
    at com.sun.javafx.sg.prism.NGNode.renderForClip(NGNode.java:2308)
    at com.sun.javafx.sg.prism.NGNode.renderRectClip(NGNode.java:2202)
    at com.sun.javafx.sg.prism.NGNode.renderClip(NGNode.java:2228)
    at com.sun.javafx.sg.prism.CacheFilter.impl_renderNodeToCache(CacheFilter.java:663)
    at com.sun.javafx.sg.prism.CacheFilter.render(CacheFilter.java:567)
    at com.sun.javafx.sg.prism.NGNode.renderCached(NGNode.java:2372)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2058)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1959)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:576)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2067)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1959)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:576)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2067)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1959)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:576)
    at com.sun.javafx.sg.prism.NGNode.renderForClip(NGNode.java:2308)
    at com.sun.javafx.sg.prism.NGNode.renderRectClip(NGNode.java:2202)
    at com.sun.javafx.sg.prism.NGNode.renderClip(NGNode.java:2228)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2061)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1959)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:576)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2067)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1959)
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:576)
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2067)
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1959)
    at com.sun.javafx.tk.quantum.ViewPainter.doPaint(ViewPainter.java:474)
    at com.sun.javafx.tk.quantum.ViewPainter.paintImpl(ViewPainter.java:320)
    at com.sun.javafx.tk.quantum.PresentingPainter.run(PresentingPainter.java:91)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
    at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125)
    at java.lang.Thread.run(Thread.java:745)

The problem is caused by the dimension of the image because it works with smaller ones. Firstly, I don't understand why the performance is so much worse with imageView compared to Swing? (In swing, I use a panel that extends a JPanel and the paintComponent method.)

Secondly, is there some kind of limit to the size of the canvas in JavaFx ?

Thank you in advance.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373

1 Answers1

3

Why your JavaFX application does not work

28k x 4k is an absolutely massive image and JavaFX 8 does not support it directly against a hardware pipeline. I am unaware of whether or not this limitation is officially documented anywhere.

See: Maximum dimensions of canvas in JavaFX.

The default JavaFX rendering pipeline will try to draw images (and canvases) to textures on a video card (for hardware accelerated compositing support). Video cards are limited in the size of textures they support, usually it is some power of two, e.g., 8192 x 8192. JavaFX (at least in previous versions that I have used), will just throw an exception if you try to render an image larger than the maximum texture size supported by the underlying video hardware.

Potential Workarounds

To work around the texture size limitation in JavaFX, process the incoming data for the image and load it up into 7 4k x 4k images (using writable images with a pixel writer). When you do this, ensure that the input data is encoded in an appropriate PixelFormat, e.g. ByteBgraPreInstance, otherwise data conversion will be required which may greatly impact the performance of your application.

There are undocumented switches for JavaFX to disable hardware rendering, and use software rendering pipeline instead. Those switches might or might not allow your application to work with large image sizes without implementing the above tiling algorithm, however, it is probably better for your application as a whole, to keep the hardware accelerated graphics pipeline enabled and code your application to work within the limitations of that pipeline (by implementing a custom tiling algorithm for large image support).

Performance Aspects

As to performance differences in a JavaFX program versus a Swing program, that is impossible to analyze without source code for both programs (and perhaps difficult to do even if source were supplied). So I will not comment further on that aspect of your question.

Canvas is a more complicated control than an ImageView and use of Canvas for this task is unlikely to give you any improved performance over the use of an ImageView.

Community
  • 1
  • 1
jewelsea
  • 150,031
  • 14
  • 366
  • 406