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.

Hi,
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
<https://javafx-jira.kenai.com/browse/RT-8799> 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 border 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)
<https://javafx-jira.kenai.com/secure/attachment/21378/image1.jpg>

package bugs.smoothness;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;

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 = ImageIO.read(is);

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) {
                e1.printStackTrace();
            }
        } 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: " + System.getProperties().get("javafx.runtime.version"));

        currentStage = stage;

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

        imageView = new ImageView(image1);
        imageView.setSmooth(true);
imageView.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                Image currentImage = imageView.getImage();
                if (currentImage == image1) {
                    imageView.setImage(image2);
                } else {
                    imageView.setImage(image1);
                }
                adjustStageTitle();
            }
        });

        Group root = new Group();
        root.getChildren().add(imageView);

        // create scene
        Scene scene = new Scene(root, WIDTH, HEIGHT);
        scene.setFill(Color.LIGHTGRAY);

        stage.setScene(scene);

        adjustStageTitle();

        // show stage
        stage.show();

        // start animation
        moveAnim = new TranslateTransition();
        moveAnim.setNode(imageView);
        moveAnim.setInterpolator(Interpolator.LINEAR);
        moveAnim.setDuration(DURATION);
        moveAnim.setCycleCount(Animation.INDEFINITE);
        moveAnim.setAutoReverse(true);
        moveAnim.setFromX(0f);
        moveAnim.setFromY(0f);
        moveAnim.setToX(WIDTH - imageView.getImage().getWidth());
        moveAnim.setToY(HEIGHT - imageView.getImage().getHeight());
        moveAnim.play();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

Reply via email to