Run the following program. Browse twice to select two reasonably large image files (such as a photo from a digital camera). Press the start button. This renders the images to a Canvas that is not attached to any scene in a worker thread. (Canvas doc's state this is legal, "If it is not attached to any scene, then it can be modified by any thread,...")
The contents of the Scene will eventually paint as all black.
/*
* Canvas usage leads to black rendering
*/
package blackui;
import java.io.File;
import java.util.concurrent.CountDownLatch;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
/**
*
* @author scott
*/
public class BlackUI extends Application {
private Thread worker;
private final SimpleBooleanProperty working = new SimpleBooleanProperty(false);
private final SimpleObjectProperty<Image> imgProp1 = new SimpleObjectProperty<>();
private final SimpleObjectProperty<Image> imgProp2 = new SimpleObjectProperty<>();
private ImageView imgView;
@Override
public void start(Stage primaryStage) {
Label img1Label = new Label("Image 1: <pick>");
Label img2Label = new Label("Image 2: <pick>");
Button browseButton = new Button("Browse...");
browseButton.setOnAction((e) -> {
FileChooser fc = new FileChooser();
File f = fc.showOpenDialog(primaryStage);
if (f != null)
if (imgProp1.get() == null || imgProp2.get() != null) {
img1Label.setText("Image 1: "+f.getName());
imgProp1.set(new Image(f.toURI().toString()));
img2Label.setText("Image 2: <pick>");
imgProp2.set(null);
} else {
img2Label.setText("Image 2: "+f.getName());
imgProp2.set(new Image(f.toURI().toString()));
}
});
Button startButton = new Button("Start");
startButton.disableProperty().bind(Bindings.or(working,Bindings.or(Bindings.isNull(imgProp1),Bindings.isNull(imgProp2))));
startButton.setOnAction(e -> {
worker = new Thread(() -> {
try {
while(!Thread.interrupted()) {
Thread.sleep(500);
doCanvasComputation();
}
} catch (InterruptedException ex) {}
finally { Platform.runLater(() -> working.set(false));}
});
worker.setDaemon(true);
working.set(true);
worker.start();
});
Button stopButton = new Button("Stop");
stopButton.setOnAction(e -> {
worker.interrupt();
});
stopButton.disableProperty().bind(Bindings.not(working));
VBox labelBox = new VBox(4, img1Label, img2Label);
HBox fieldBox = new HBox(8, labelBox, browseButton, startButton, stopButton);
imgView = new ImageView();
imgView.setFitWidth(256);
imgView.setFitHeight(256);
imgView.setPreserveRatio(true);
BorderPane root = new BorderPane();
root.setTop(fieldBox);
root.setCenter(imgView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
// We are using Canvas to do off-screen image processing
private void doCanvasComputation() throws InterruptedException {
Image img1 = imgProp1.get();
Image img2 = imgProp2.get();
Canvas canvas = new Canvas(img1.getWidth(), img1.getHeight());
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setGlobalBlendMode(BlendMode.SRC_OVER);
gc.drawImage(img1, 0, 0);
gc.setGlobalBlendMode(BlendMode.DIFFERENCE);
gc.drawImage(img2, 0, 0);
CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(() -> {
WritableImage result = canvas.snapshot(null, null);
imgView.setImage(result);
latch.countDown();
});
latch.await();
// Here we would get a pixel reader to process the result image
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
The contents of the Scene will eventually paint as all black.
/*
* Canvas usage leads to black rendering
*/
package blackui;
import java.io.File;
import java.util.concurrent.CountDownLatch;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
/**
*
* @author scott
*/
public class BlackUI extends Application {
private Thread worker;
private final SimpleBooleanProperty working = new SimpleBooleanProperty(false);
private final SimpleObjectProperty<Image> imgProp1 = new SimpleObjectProperty<>();
private final SimpleObjectProperty<Image> imgProp2 = new SimpleObjectProperty<>();
private ImageView imgView;
@Override
public void start(Stage primaryStage) {
Label img1Label = new Label("Image 1: <pick>");
Label img2Label = new Label("Image 2: <pick>");
Button browseButton = new Button("Browse...");
browseButton.setOnAction((e) -> {
FileChooser fc = new FileChooser();
File f = fc.showOpenDialog(primaryStage);
if (f != null)
if (imgProp1.get() == null || imgProp2.get() != null) {
img1Label.setText("Image 1: "+f.getName());
imgProp1.set(new Image(f.toURI().toString()));
img2Label.setText("Image 2: <pick>");
imgProp2.set(null);
} else {
img2Label.setText("Image 2: "+f.getName());
imgProp2.set(new Image(f.toURI().toString()));
}
});
Button startButton = new Button("Start");
startButton.disableProperty().bind(Bindings.or(working,Bindings.or(Bindings.isNull(imgProp1),Bindings.isNull(imgProp2))));
startButton.setOnAction(e -> {
worker = new Thread(() -> {
try {
while(!Thread.interrupted()) {
Thread.sleep(500);
doCanvasComputation();
}
} catch (InterruptedException ex) {}
finally { Platform.runLater(() -> working.set(false));}
});
worker.setDaemon(true);
working.set(true);
worker.start();
});
Button stopButton = new Button("Stop");
stopButton.setOnAction(e -> {
worker.interrupt();
});
stopButton.disableProperty().bind(Bindings.not(working));
VBox labelBox = new VBox(4, img1Label, img2Label);
HBox fieldBox = new HBox(8, labelBox, browseButton, startButton, stopButton);
imgView = new ImageView();
imgView.setFitWidth(256);
imgView.setFitHeight(256);
imgView.setPreserveRatio(true);
BorderPane root = new BorderPane();
root.setTop(fieldBox);
root.setCenter(imgView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
// We are using Canvas to do off-screen image processing
private void doCanvasComputation() throws InterruptedException {
Image img1 = imgProp1.get();
Image img2 = imgProp2.get();
Canvas canvas = new Canvas(img1.getWidth(), img1.getHeight());
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setGlobalBlendMode(BlendMode.SRC_OVER);
gc.drawImage(img1, 0, 0);
gc.setGlobalBlendMode(BlendMode.DIFFERENCE);
gc.drawImage(img2, 0, 0);
CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(() -> {
WritableImage result = canvas.snapshot(null, null);
imgView.setImage(result);
latch.countDown();
});
latch.await();
// Here we would get a pixel reader to process the result image
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
- duplicates
-
JDK-8094665 Scene background can disappear if the back buffer is ever lost due to low texture memory
- Resolved