JavaFX-Load Image in the background

The default is loaded synchronously

When an instance of ʻImage` is created normally, the image is loaded synchronously (no processing is returned until the loading is completed).

If the image is small, there is no particular problem, but if a large image is displayed, the processing is stopped for a while, which may impair the operability.

Load in the background

ʻTo load an image in the Image instance in the background, specify truefor the constructor argumentbackgroundLoading. There are two constructors that can specify backgroundLoading`.

If you create an instance with true for backgroundLoading, the image will be loaded asynchronously. As a result, the process can proceed without stopping.

View loading progress

If you simply put the load in the background, the image will not be displayed until the loading is completed.

If the size is large and it takes a long time to load, the fact that no image is displayed may be disturbing to the user. Therefore, it may be good to display the progress of how much the load is progressing with a progress bar or the like.

ʻImage` class can know the progress of loading progress Provides properties. If you use this, you can display the progress of loading relatively easily.

Implementation

WS000000.jpg

main.fxml


<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>

<BorderPane style="-fx-padding: 10px;" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.javafx.MainController">
   <center>
      <VBox alignment="CENTER" minHeight="0.0" minWidth="0.0" BorderPane.alignment="CENTER">
         <children>
            <ProgressBar fx:id="progressBar" maxWidth="1.7976931348623157E308" progress="0.0" />
            <ImageView fx:id="imageView" fitHeight="200.0" fitWidth="300.0" pickOnBounds="true" preserveRatio="true" />
         </children>
      </VBox>
   </center>
</BorderPane>

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ProgressBar;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.net.URL;
import java.nio.file.Paths;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    @FXML
    private ProgressBar progressBar;
    @FXML
    private ImageView imageView;

    public void initStage(Stage stage) {
        stage.setWidth(500);
        stage.setHeight(400);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        progressBar.managedProperty().bind(progressBar.visibleProperty());
        progressBar.visibleProperty().bind(progressBar.progressProperty().lessThan(1));
        
        imageView.managedProperty().bind(imageView.visibleProperty());
        imageView.visibleProperty().bind(progressBar.progressProperty().isEqualTo(1));
        
        Image image = new Image(Paths.get("./image/shirakawago.jpg ").toUri().toString(), true);
        progressBar.progressProperty().bind(image.progressProperty());
        imageView.setImage(image);
    }
}

Execution result

javafx.gif

It's a bit quick and confusing, but the progress bar is displayed while the image is loading, and the image is displayed when the loading is complete.

Description

WS000000.jpg

--Added ProgressBar next to ʻImageView` for image display

MainController.java


        progressBar.managedProperty().bind(progressBar.visibleProperty());
        progressBar.visibleProperty().bind(progressBar.progressProperty().lessThan(1));
        
        imageView.managedProperty().bind(imageView.visibleProperty());
        imageView.visibleProperty().bind(progressBar.progressProperty().isEqualTo(1));

--Controlling each visible property to show only the progress bar during loading and only the image when loading is complete --Control of the visible property is achieved by binding to the value of the progress property of the progress bar. --The progress bar is displayed only when progress is less than 1 --The image is displayed only when progress is 1.

MainController.java


        Image image = new Image(Paths.get("./image/shirakawago.jpg ").toUri().toString(), true);
        progressBar.progressProperty().bind(image.progressProperty());

--Specify true for backgroundLoading in the constructor argument when generating ʻImage --Binding theprogress property of ʻImage to the progress property of the progress bar

Load in advance

merit

Loading in the background can be used not only by loading the image currently being displayed, but also by preloading the image to be displayed next.

In this case, only the ʻImage instance is generated behind the scenes, and when switching images, the ʻImage instance is replaced with thesetImage ()of ʻImageView`.

image


@FXML
private ImageView imageView;
private Image nextImage;

public void initialize(URL location, ResourceBundle resources) {
    //Load the next image in the background
    nextImage = new Image("next-image.jpg ", true);

    Image initialImage = new Image("initial-image.jpg ", true);
    imageView.setImage(initialImage);
}

@FXML
public void nextPage() {
    //Set the next loaded image
    imageView.setImage(nextImage);
}

Even heavy images can be displayed quickly by loading them in advance, which may lead to an improvement in operability.

memory usage

It should be noted that pre-loading may improve display speed, but at the cost of increased memory consumption.

In particular, it should be noted that there is a big difference between the size of an image on disk and the size when loaded into memory. On a disk, for example, a JPEG image has a significantly compressed size, whereas it is uncompressed when loaded into memory with ʻImage`.

I will investigate how different it actually is.

javafx.jpg

Here's the size on disk, about 6.5 MB.

Load this image with ʻImage, then take a heap dump to see how much memory the image loaded by ʻImage uses. The Eclipse Memory Analyzer was used to investigate the heap dump.


javafx.jpg

The platformImage in ʻImage` looks like that, so go look inside it.


javafx.jpg

com.sum.prism.Image looks like that, so go look inside.


javafx.jpg

The pixel size of the image is stored in width and height. Furthermore, it seems that pixelBuffer stores image data in a byte array, so go look inside it.


javafx.jpg

It looks like a bingo. capacity is 36366672 (about 35 MB). When I looked it up, hb was an array of byte, and it seems that all the image data was stored.

This size of 36,636,672 exactly matches the size of" vertical x horizontal x 3 "in the image. (4,288 * 2,848 = 12,212,224, 12,212,224 * 3 = 36,636,672

In other words, all the RGB (3byte) data of all pixels is loaded.

The size on disk is about 6.5 MB, but when loaded into memory it is quite large at 35 MB.

I think that the size difference between the disk and memory will vary depending on the compression format of the original image and the presence or absence of the alpha channel (transparency).

Reduce the size and load

ʻImage` can be loaded by specifying the size of the image.

By using this, the image can be loaded in a smaller size than the original, so memory consumption can be reduced. However, please note that it will not be displayed with the same quality as the original image because it will be loaded in a small size.

Summary

Loading in the background may improve the display speed, but you should also pay attention to the memory consumption when using the cache by preloading.

Recommended Posts

JavaFX-Load Image in the background
Display the background image in the production environment
Rails6: Extract the image in Action Text
[Swift] How to set an image in the background without using UIImageView.
Put the image obtained from http in NotificationCompat
Background image transparency (layering the image with your favorite color)
[Rails 6] How to set a background image in Rails [CSS]
[Rails] How to display an image in the view
Install yarn in docker image
Install the plugin in Eclipse
Java sets the background color and background image for PowerPoint documents
Create a private repository in Amazon ECR and push/pull the image
[Android Studio] Set an arbitrary image for the application background [Java]
Replace preview by uploading by clicking the image in file_field of Rails
Insert an image in the verification email when authenticating devise email --Notes--
Access the network interface in Java
Guess the character code in Java
Exceptions encountered in the AWS SDK
Pass the i18n locale to JavaScript
Apache Camel in the cloud-native era
About the symbol <%%> in Rails erb
Specify the java location in eclipse.ini
Let's verify the image search filter
Automatically scroll the background with libGDX
Unzip the zip file in Java
Use the Findbugs plugin in Eclipse
Place in the middle with css
Parsing the COTOHA API in Java
Hit the Docker API in Rust
Expression used in the fizz_buzz problem
Include image resources in jar file
Yes, let's preview the image. ~ part5 ~
Order of processing in the program
What are the rules in JUnit?
Image processing: Let's play with the image
Call the super method in Java
What to do if the background image is not applied after deployment
[Rails] About the error that the image is not displayed in the production environment
Change the save destination of the image to S3 in the Rails app. Part 2