I am developing a small medical reader using javaFX that render almost 24 linecharts using canvas.
JavaFX linechart component does not perform well so I switched to Canvas and performance was enhanced greatly. However, still can\t achieve required FPS. Here's the situation:
1 - 24 Canvas on the same screen (contained in a VBox)
2 - Each canvas render a linechart via gc.beginPath, lineTo...
Here's the code I am using /////////////
gc.beginPath();
int dataLength = data.getData().length;
int k = 1;
for(double f: data.getData()) {
gc.lineTo(getXPixel(k, dataLength), getYPixel(f, maxPt));
k++;
}
gc.stroke();
///////////////// I am reading the data from arrays in memory using threads and updating the canvas using platform.runlater. A single linechart could contains 5000 points
I know the performance issue is coming from gc.lineTo and gc.stroke...because when I comment //gc.stroke() the reading is fast without rendering the canvas (stroking the lines).
Is there a way to enhance performance?
https://i.stack.imgur.com/kniRI.jpg
Here's a working code
import com.sun.javafx.perf.PerformanceTracker;
import java.util.Random;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
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.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* @author abadran
*/
public class CanvasLineStress extends Application {
Canvas[] charts = new Canvas[10];
double CHART_YPADDING = 26;
final static double CHART_WIDTH = 1000;
final static double CHART_HEIGHT = 50;
final static int DATA_PTS = 5000;
Label fpsLabel = new Label("Frame : ");
private int currentFrame = 0;
@Override
public void start(Stage primaryStage) {
AnimationTimer playCharts = new AnimationTimer() {
@Override
public void handle(long now) {
renderCharts();
fpsLabel.setText("Frame: " + currentFrame);
currentFrame++;
}
};
VBox chartContainer = new VBox(5);
chartContainer.getChildren().add(fpsLabel);
for(int i = 0; i < charts.length; i++) {
charts[i] = new Canvas(CHART_WIDTH, CHART_HEIGHT);
chartContainer.getChildren().add(charts[i]);
}
Button btn = new Button();
btn.setText("Run Charter...");
btn.setOnAction((ActionEvent event) -> {
playCharts.start();
});
StackPane root = new StackPane();
root.getChildren().addAll(chartContainer, btn);
Scene scene = new Scene(root, 1200, 900);
primaryStage.setTitle("Canvas Stress Test");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private void renderCharts() {
for (Canvas chart : charts) {
GraphicsContext gc = chart.getGraphicsContext2D();
gc.clearRect(0, 0, chart.getWidth(), chart.getHeight());
gc.beginPath();
for(int k = 0; k < DATA_PTS; k++) {
Random r = new Random();
double f = -10.0 + r.nextDouble() * 40.0;
gc.lineTo(getXPixel(k, DATA_PTS, CHART_WIDTH), getYPixel(f, 50, CHART_HEIGHT));
}
// comment the stroke action and frame rate will increase dramatically...
gc.stroke();
}
}
private double getXPixel(int x, int dataLength, double chartWidth) {
return x * (chartWidth / dataLength);
}
private double getYPixel(double y, double maxPt, double chartHeight) {
return chartHeight - (((chartHeight - CHART_YPADDING) / maxPt) * y) - CHART_YPADDING;
}
}