JavaFX with Nashorn Canvas example

Following on from my previous examination of the performance and features of JavaFX Canvas and HTML5 Canvas here and inspired by Jim Laskey’s blogs about Nashorn, I decided I would whip-up a quick example of combining JavaFX Canvas and Nashorn based on an existing HTML5 Canvas example.  I recently found a nice little HTML5 Canvas example and that is what I have implemented here in Nashorn.

For those that don’t know, Nashorn is a JavaScript engine that runs on the JVM and is to be included as part of the upcoming Java 8 release.  It will perform far better than the existing major Java-based JavaScript engine Rhino because it utilises the new invokedynamic bytecode added to JDK 8 allowing it to better exploit the power of the JVM.  Nashorn is based on ECMAScript-262 and is fully compliant (which may make it unique in the world of JS engines).

Here’s a short video showing this animation in action:

On my machine, the HTML5 example achieves about 175FPS.  The JavaFX/Nashorn version starts off with a lower framerate but as HotSpot kicks-in over time, this rate increases to almost 200FPS on the same machine.  The framerate that can be seen in the video hovers around 166FPS but this is a symptom of running the video capture software at the same time.

So let’s have a brief look at the code:

var File                 = Java.type("java.io.File");
var Image                = Java.type("javafx.scene.image.Image");
var Color                = Java.type("javafx.scene.paint.Color");
var Canvas               = Java.type("javafx.scene.canvas.Canvas");
var BorderPane           = Java.type("javafx.scene.layout.BorderPane");
var StackPane            = Java.type("javafx.scene.layout.StackPane");
var Scene                = Java.type("javafx.scene.Scene");
var Font                 = Java.type("javafx.scene.text.Font");
var FontSmoothingType    = Java.type("javafx.scene.text.FontSmoothingType");
var Text                 = Java.type("javafx.scene.text.Text");
var AnimationTimer       = Java.type("javafx.animation.AnimationTimer");
var PerformanceTracker   = Java.type("com.sun.javafx.perf.PerformanceTracker");
var AnimationTimerExtend = Java.extend(AnimationTimer);

var WIDTH = 1200;
var HEIGHT = 900;

var canvas = new Canvas(WIDTH, HEIGHT);
var imageUrl = fileToURL("avatar.png");
var img = new Image(imageUrl);
var font = new Font("Arial", 16);
var fpsLabel = new Text();

function renderFrame() {
    var t = new Date().getTime();
    var gc = canvas.graphicsContext2D;

    gc.setFill(Color.web("#cccccc"));
    gc.fillRect(0, 0, WIDTH, HEIGHT);
    gc.setStroke(Color.web("#000000"));

    gc.setLineWidth(1);
    gc.strokeRect(5, 5, WIDTH - 10, HEIGHT - 10);

    var c = 200;
    var msc= 0.5 * HEIGHT / img.height;
    var sp0 = 0.003;
    for (var h = 0; h < c; h++) {
        gc.setTransform(1, 0, 0, 1, 0, 0);
        var yh = h / (c - 1);
        gc.translate((0.5 + Math.sin(t * sp0 + h * 0.1) / 3) * WIDTH, 25 + (HEIGHT * 3 / 4 - 40) * (yh * yh));
        var sc = 30 / img.height + msc * yh * yh;
        gc.rotate(90 * Math.sin(t * sp0 + h * 0.1 + Math.PI));
        gc.scale(sc, sc);
        gc.drawImage(img, -img.width / 2, -img.height / 2);
     }

    gc.setTransform(1, 0, 0, 1, 0, 0);

    fpsLabel.setText(Math.floor(tracker.getAverageFPS() + 0.5) + " FPS");
}

function fileToURL(file) {
    return new File(file).toURI().toURL().toExternalForm();
}

var timer = new AnimationTimerExtend() {
    handle: function handle(now) {
        renderFrame();
    }
};

var stack = new StackPane();
var pane = new BorderPane();
pane.setCenter(canvas);

fpsLabel.setManaged(false);
fpsLabel.setFont(font);
fpsLabel.setFontSmoothingType(FontSmoothingType.LCD);
fpsLabel.setX(10);
fpsLabel.setY(24);

stack.getChildren().add(pane);
stack.getChildren().add(fpsLabel);

$STAGE.scene = new Scene(stack);
var tracker = PerformanceTracker.getSceneTracker($STAGE.scene);

timer.start();

The animation can be launched using the following command:

jjs -fx -Djavafx.animation.fullspeed=true -scripting sprites.js

The command jjs (which you will find in your Java bin directory) is used to launch Nashorn and the -fx argument tells Nashorn that it is to launch a JavaFX application.  The argument -Djavafx.animation.fullspeed=true lets JavaFX run at the maximum framerate it can support.  Without this option, the animation timer will be capped at 60FPS based on the frequency of the “pulse”.

Once again I have found that the performance of JavaFX is at least as good as any HTML5 Canvas implementation and, now with Nashorn, it is even easier to convert web-based animations into JavaFX animations.  Nashorn will bring high-performance JavaScript to the JVM and enable advanced scripting for Java, JavaFX and any JVM-based language applications.

You can try it for yourself with the JavaFX Nashorn Canvas Source including the image file used, the Nashorn script and a BAT file to launch the animation on Windows.

Note: You will require Java 8 to run this example.  This script was tested with JDK 8 b105 which is available here.

Please leave feedback to let me know how this example works for you and any other comments you may have!

Just my 2 bits

Felix

Posted on September 8, 2013, in JavaFX and tagged , , , , . Bookmark the permalink. 13 Comments.

  1. Good Job.
    Is it possible to run it with JavaScript Rhino engine?

    I try it on my PC (Windows7, i7-2600@3.40GHz, 8GB RAM, ATI AMD Radeon HD 6350), here the results:
    Chrome: max 143 FPS
    Java 8ea b103: max 44 FPS
    Java 8ea b106: max 44 FPS

    • Hi scevra,

      Thanks for the feedback.

      I am sure it would be possible to run this with Rhino with some small modifications but I am not a Rhino afficando and I haven’t tried it. I would expect though that the result would be pretty much the same (i.e. FPS) even though Nashorn is faster than Rhino because the performance here is limited more by rendering performance than JavaScript performance.

      The results you posted are both surprising and disappointing. Are you sure you are running the script with the -Djavafx.animation.fullspeed=true argument? I get much lower FPS numbers if I don’t specifiy this option.

      Felix

  2. Without the option -Djavafx.animation.fullspeed=true the FPS are around 34 (with max FPS 39). [Java 8ea b106]

    Scevra

  3. With Linux and nVidia – only clear screen visible

    http://imageshack.us/photo/my-images/62/ug96.png/

  4. @m1k0 Someone reported the same thing on MacOS. Which build of Java 8 are you using? What are the details of the graphics card and OS?

  5. At 1024×768 it tops out at 32 FPS on my laptop. Windows 7, jdk 8 b108, and there’s no graphics card. It’s using the built-in graphics of my Intel i5-2520M cpu. I get about the same speed with or without -Djavafx.animation.fullspeed=true

  6. Hi why you people compare JavaFX with html5. Test with Flash and Silverlight then we will believe it that javaFX is good technology

    • Well I don’t see any point in comparing JavaFX with “dead” technologies such as Flash or Silverlight. On the other hand, HTML5 is touted as the future of software development and is often claimed to be “the only technology you need on any device”.

      Given that I have shown more than once that JavaFX out-performs HTML5, I think that is significant and something the world should be aware of.

  1. Pingback: Java desktop links of the week, September 9 « Jonathan Giles

  2. Pingback: JavaFX links of the week, September 9 // JavaFX News, Demos and Insight // FX Experience

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 141 other followers

%d bloggers like this: