I'm trying to display a million ovals in a canvas dynamicly every second. I use a thread as the rendering thread, but my problem is, after a few secondes, the canvas freezes and stops displaying. I guess that the buffer is full so it can't display anymore, but how can I clear up the buffer? The test source code is as follow:
public class Main extends Application {
public void start(Stage primaryStage) {
primaryStage.setTitle("Drawing Operations Test");
Group root = new Group();
Canvas canvas = new Canvas(800, 800);
GraphicsContext gc = canvas.getGraphicsContext2D();
drawShapes(gc);
root.getChildren().add(canvas);
primaryStage.setScene(new Scene(root));
primaryStage.show();
Task task2 = new Task<Void>()
{
public synchronized Void call() throws Exception {
while (true) {
Thread.sleep(1000);
Canvas canvas = gc.getCanvas();
canvas.getGraphicsContext2D().clearRect(0, 0, canvas.getHeight(), canvas.getWidth());
drawShapes(canvas.getGraphicsContext2D());
}
}
};
Thread t = new Thread(task2);
t.start();
}
private void drawShapes(GraphicsContext gc) {
gc.setFill(Color.GREEN);
gc.setStroke(Color.BLUE);
gc.setLineWidth(1);
double widthOval=1;
double heightOval = 1;
for (int i = 0; i < 500; ++i) {
for (int j = 0; j < 500; ++j) {
if (Math.random() < 0.5) {
gc.fillOval(i * widthOval, j * heightOval, widthOval, heightOval);
}
else {
gc.strokeOval(i * widthOval, j * heightOval, widthOval, heightOval);
}
}
}
}
public static void main(String[] args) {
launch(args);
}
}
Can anyone help me?
Thanks in advance,
Pierre
What is (probably) going wrong and how to fix it
Don't use a Task and a separate thread for the drawing. Modifications to the scene graph (including a canvas), must be done on the JavaFX application thread. Instead use a Timeline (which will implicitly execute it's keyframe code on the JavaFX application thread). If you must use a task, then at least use Platform.runLater to surround your calls into the JavaFX API that modify the active scene graph.
Potential scaling issue
A million ovals per second is probably a lot to try to draw in a single frame. You may wish to run some benchmark tests to see what a usable number is for your target platforms (though my sample below does seem to execute fine without a performance issue on a 2014 Macbook Pro).
Sample Code
Here is an updated sample with a Timeline that you could try. It seems to work fine for me (Java 8u60, OS X 10.9.5) - rendering 250,000 circles each second:
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.canvas.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
public class LotsaCircles extends Application {
private static final double SIZE = 500;
public void start(Stage stage) {
Canvas canvas = new Canvas(SIZE, SIZE);
Timeline timeline = new Timeline(
new KeyFrame(
Duration.seconds(0),
event -> drawShapes(canvas.getGraphicsContext2D())
),
new KeyFrame(Duration.seconds(1))
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
stage.setScene(new Scene(new Group(canvas)));
stage.show();
}
private void drawShapes(GraphicsContext gc) {
gc.clearRect(0, 0, SIZE, SIZE);
gc.setFill(Color.GREEN);
gc.setStroke(Color.BLUE);
gc.setLineWidth(1);
double widthOval = 1;
double heightOval = 1;
for (int i = 0; i < SIZE; ++i) {
for (int j = 0; j < SIZE; ++j) {
if (Math.random() < 0.5) {
gc.fillOval(i * widthOval, j * heightOval, widthOval, heightOval);
}
else {
gc.strokeOval(i * widthOval, j * heightOval, widthOval, heightOval);
}
}
}
}
public static void main(String[] args) {
launch(args);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.