Ilja Tarasovs
Ilja Tarasovs

Reputation: 211

How to update part of BufferedImage and display it with javafx

enter image description here I have an image. At the beginning I want the image to be displayed with only the first frame. On the keyboard with 'u' key pressed, drink water and load the second frame. If I understand correctly the whole image should be loaded at the beginning. And then I need to set new y.
How to complete this task? What is the right way to display this image?
My code in:

package testDesktopUi;

import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class DesktopUi extends Application {
    private static int y = 0;
    private static BufferedImage bufferedImage;
    private static final ImageView imageView = new ImageView();
    private static final HBox root = new HBox();

    @Override
    public void start(Stage primaryStage) throws IOException {
        String path = "Bottle.png";

        bufferedImage = ImageIO.read(new File(path)).getSubimage(0, 0, 32, 32);
        Image image = SwingFXUtils.toFXImage(bufferedImage, null);
        imageView.setImage(image);

        root.getChildren().add(imageView);
        Scene scene = new Scene(root);

        scene.setOnKeyPressed(key -> {
            if (key.getCode() == KeyCode.U) {
                System.out.println("u pressed");
                updateImage(bufferedImage);
                System.out.println(y);
            }
        });

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private static void updateImage(BufferedImage bufferedImage) {
        int maxHeight = 352;
        if (y + 32 >= maxHeight) {
            y = 0;
        } else {
            y += 32;
        }
        //?? bufferedImage.getSubimage(0, y, 32, 32);
    }
}

Upvotes: 0

Views: 342

Answers (1)

James_D
James_D

Reputation: 209289

If you're using JavaFX, stick just to the JavaFX image API: there is no need to first load an AWT BufferedImage and convert it to a JavaFX image.

To display portions of an image, you can create an ImageView from the image and set the ImageView's viewport.

import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;


public class DesktopUi extends Application {
    private int y = 0;
    private final ImageView imageView = new ImageView();
    private final HBox root = new HBox();

    @Override
    public void start(Stage primaryStage) {
        String path = "Bottle.png";

        // Assumes Bottle.png is in the same package as the current class:
        Image image = new Image(getClass().getResource("Bottle.png").toExternalForm());
        imageView.setImage(image);

        // display only a portion of the image:
        imageView.setViewport(new Rectangle2D(0, y, 32, 32));

        root.getChildren().add(imageView);
        Scene scene = new Scene(root);

        scene.setOnKeyPressed(key -> {
            if (key.getCode() == KeyCode.U) {
                System.out.println("u pressed");
                System.out.println(y);
                updateImage();
            }
        });

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void updateImage() {

        // update y 
        int maxHeight = 352;
        if (y + 32 >= maxHeight) {
            y = 0;
        } else {
            y += 32;
        }

        // update portion of image displayed
        imageView.setViewport(new Rectangle2D(0, y, 32, 32));
    }

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

Note this also lends itself nicely to animations:

@Override
public void start(Stage primaryStage) {

    int numSprites = 11 ;

    Image image = new Image(getClass().getResource("Bottle.png").toExternalForm());
    imageView.setImage(image);

    IntegerProperty spriteIndex = new SimpleIntegerProperty();
    spriteIndex.addListener((obs, oldIndex, newIndex) -> System.out.println(newIndex));
    imageView.viewportProperty().bind(Bindings.createObjectBinding(
            () -> new Rectangle2D(0, spriteIndex.get() * 32 , 32, 32),
            spriteIndex
    ));

    Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(5),
            new KeyValue(spriteIndex, numSprites - 1)));
    timeline.setCycleCount(Animation.INDEFINITE);
    timeline.play();

    root.getChildren().add(imageView);
    Scene scene = new Scene(root);

    primaryStage.setScene(scene);
    primaryStage.show();
}

Upvotes: 3

Related Questions