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.
ʻTo load an image in the Image instance in the background, specify
truefor the constructor argument
backgroundLoading. 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.
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.
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);
}
}
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.
--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 the
progress property of ʻImage
to the progress
property of the progress bar
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.
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.
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.
The platformImage
in ʻImage` looks like that, so go look inside it.
com.sum.prism.Image
looks like that, so go look inside.
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.
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).
ʻ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.
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