JavaFX Canvas绘制了很多对象(< 250.000 => 500x500px)

时间:2013-11-06 19:25:13

标签: canvas drawing javafx 2d

这是我对Stackoverflow的第一个问题,所以请对我温和:)

我最近发现JavaFX是一种为Java应用程序设计GUI的新方法。目前我正在开发一个项目,需要在Canvas上绘制最多500x500(250.000)个对象,这些对象可以从1x1px到20x20px大(都具有相同的大小)。 可悲的是,表现并不是很好。绘制500x500矩形的字段需要10秒钟。 我做了一个示例应用程序来演示这个问题。

为什么表现如此糟糕?由于绘图发生在UI线程中,因此UI将被阻止,直到绘图完成。 我有2个方法:

  1. 快速绘图。
  2. 在单独的线程中绘制缓冲区对象,并在画布上显示结果。
  3. 我在画布上绘制图像之前写入BufferedImage的实验失败了(画布保持空白)。

    感谢您花时间帮助研究此主题:)

    sample.fxml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import java.lang.*?>
    <?import javafx.geometry.Insets?>
    <?import javafx.scene.control.*?>
    <?import javafx.scene.control.Button?>
    <?import javafx.scene.control.Label?>
    <?import javafx.scene.layout.*?>
    <?import javafx.scene.layout.GridPane?>
    
    <AnchorPane prefHeight="275.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="sample.Controller">
      <children>
        <ScrollPane fx:id="scrollPane" content="$null" prefHeight="237.0" prefWidth="300.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="38.0" />
        <Button layoutX="14.0" layoutY="14.0" mnemonicParsing="false" onAction="#draw" text="Draw" />
      </children>
    </AnchorPane>
    

    Controller.java:

    package sample;
    
    import javafx.event.ActionEvent;
    import javafx.fxml.FXML;
    import javafx.scene.canvas.Canvas;
    import javafx.scene.canvas.GraphicsContext;
    import javafx.scene.control.ScrollPane;
    import javafx.scene.paint.Color;
    
    import java.util.Random;
    
    public class Controller {
    
        @FXML
        private ScrollPane scrollPane;
    
        Canvas canvas;
        @FXML
        protected void draw(ActionEvent event) {
        canvas = new Canvas(500,500);
        scrollPane.setContent(canvas);
        GraphicsContext gc = canvas.getGraphicsContext2D();
            Random r = new Random();
    
            for(int i = 0; i<=500; i++) {
                for(int j = 0; j<=500; j++) {
                    if (r.nextBoolean()) {
                        gc.setFill(Color.BLACK);
                    } else {
                        gc.setFill(Color.BLUE);
                    }
                    gc.fillRect(i, j, i+1, j+1);
                }
            }
        }
    }
    

    Main.java:

    package sample;
    
    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.scene.control.ScrollPane;
    import javafx.stage.Stage;
    
    public class Main extends Application {
    
        @Override
        public void start(Stage primaryStage) throws Exception{
            Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
            ScrollPane scrollPane = (ScrollPane) root.lookup("#scrollPane");
            primaryStage.setTitle("Hello World");
            primaryStage.setScene(new Scene(root, 300, 275));
            primaryStage.show();
        }
    
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

1 个答案:

答案 0 :(得分:4)

我怀疑根本原因可能与每次绘制调用创建画布上下文有关。如果您只填充一次画布并保留对它的引用,您可能会看到改进。换句话说:

protected void draw(...) { canvas = new Canvas(...); }

应该转移到初始化。

public void start(...) { ... /* Other initialization. */ ... ; canvas = new Canvas(...); }

其他一切都可以保持不变。

编辑1:我知道这个问题已经过时了,但它在谷歌上排名很高,应该有利于繁荣。

编辑2:在进一步调查中,我认为这与绘制调用的速度有关,而不是实际获取画布。链接Java Hardware Acceleration有更多信息。总之,我会考虑强制硬件加速。您可以使用someCanvas.getGraphicsConfiguration() + .getBufferCapabilities() + .isPageFlipping()的增量添加来检查它是否对您的呼叫有效。