JavaFX graphics performance and suitability for advanced animations

Dr. Michael Paus mp at
Sat Jun 1 05:11:01 PDT 2013

On 31.05.13 23:35, Richard Bair wrote:
> We should start simple and work our way up. Since we've spent most of our time working on raw frame rates, perhaps it would be best to face down the jitter problem first. Lets start with something simple: a basic translate transition of a rectangle, and see how that goes.
I'd like to add a very similar problem - a translate transition of a 
simple image.
This is something you see very often in animation examples but although the
JavaFX problem associated with this has already been reported many years ago
(on monday it will be exactly 3 years) this has not been fixed until now.
(I just checked it with JDK8 b92) My original report
<> was marked as a duplicate,
although I am still not sure it really is but I can't check this because 
the explanation
of the possible reason in RT-6933 is kept secret.

Anyway - you still can't animate images smoothly with JavaFX out of the box
because although the image itself is placed at sub-pixel positions the 
of the image is clipped to exact pixel boundaries which makes the 
animation look bad.

I have attached code to show this. You can also switch between a smooth 
and a
non-smooth animation by clicking into the image with the mouse. When you
click into the image I apply a little hack so the animation becomes 
smooth. But
one should not be forced to use such tricks with a framework like JavaFX 
in order
to get a smooth animation for such a trivial use case.

Here comes the code: (You can use the image from the original bug report)

package bugs.smoothness;

import java.awt.image.BufferedImage;

import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;

import javax.imageio.ImageIO;

public class ImageAnimation extends Application {
     private static final String INPUT_IMAGE = "image1.jpg";

     private static final double WIDTH = 1024;
     private static final double HEIGHT = 768;

     private static final Duration DURATION = Duration.millis(100000);

     private Image image1;
     private Image image2;

     private ImageView imageView;

     private TranslateTransition moveAnim;

     private Stage currentStage;

     public Image createImage(String relFilePath, boolean smooth) {
         InputStream is = getClass().getResourceAsStream(relFilePath);
         Image image = null;

         if (smooth) {
             try {
                 BufferedImage i1 =;

                 BufferedImage i2 = new BufferedImage(i1.getWidth()+2, 
i1.getHeight()+2, BufferedImage.TYPE_INT_ARGB);
                 i2.getGraphics().drawImage(i1, 1, 1, new 
java.awt.Color(0, 0, 0, 0), null);

                 image = SwingFXUtils.toFXImage(i2, null);
             } catch (IOException e1) {
         } else {
             image = new Image(is);

         if (image != null && !image.isError()) {
             return image;
         } else {
             System.err.println("Could not open " + relFilePath);
             return null;

     public void adjustStageTitle() {
         if (imageView.getImage() == image1) {
             currentStage.setTitle("image1 - standard border");
         } else {
             currentStage.setTitle("image2 - one pixel transparent border");

     @Override public void start(Stage stage) {
         System.out.println("javafx.runtime.version: " + 

         currentStage = stage;

         image1 = createImage(INPUT_IMAGE, false);
         image2 = createImage(INPUT_IMAGE, true);

         imageView = new ImageView(image1);
         imageView.addEventHandler(MouseEvent.MOUSE_CLICKED, new 
EventHandler<MouseEvent>() {
             public void handle(MouseEvent event) {
                 Image currentImage = imageView.getImage();
                 if (currentImage == image1) {
                 } else {

         Group root = new Group();

         // create scene
         Scene scene = new Scene(root, WIDTH, HEIGHT);



         // show stage;

         // start animation
         moveAnim = new TranslateTransition();
         moveAnim.setToX(WIDTH - imageView.getImage().getWidth());
         moveAnim.setToY(HEIGHT - imageView.getImage().getHeight());;

     public static void main(String[] args) {

More information about the openjfx-dev mailing list