代码之家  ›  专栏  ›  技术社区  ›  NotZack

只绘制应该在屏幕上的节点?

  •  0
  • NotZack  · 技术社区  · 5 年前

    我正在JavaFX中使用场景图(而不是画布)进行模拟,并且在屏幕上只绘制我需要的东西时遇到了问题。

    这个模拟中有1000多万个节点,但是用户只需要在屏幕上同时看到这些节点的一小部分(最多16万个节点)。我关心的所有节点都是400x400 ImageViews .

    每个节点都是 Group (节点块)包含大约40000个节点,因此每次需要显示4个或更少的节点块。为了显示这些“节点块”,它们被添加到静态 Pane 该窗格位于根节点中,这是一个 .

    所以我从第一个家长到最后一个孩子的图表是这样的:

    根节点 \显示器 窗格 \(许多)节点块 \<=40000个 图像视图

    由于显示窗格根据用户输入不断地移动(平移和重缩放),而且节点太多,所以应用程序的运行速度没有达到我想要的速度。JavaFX很难同时跟踪1000多万个节点,因此我的解决方案是从显示窗格中删除所有“节点块”;将它们保存在哈希图中,直到需要绘制它们为止。

    每个“节点块”都有自己的 LayoutX LayoutY 设置为均匀分布在网格中的显示窗格中,如下所示:

    Node chunk display

    在这个例子中,我需要抓取并显示“node chunk”7、8、12和13,因为这是用户看到的。

    以下是手动添加了“node chunk”0的屏幕截图。绿黄色是放置“节点块”1、5和6的位置。

    chunk 1

    我的问题是:由于“节点块”在需要时才会添加到显示窗格中,因此我无法参照用户看到的显示窗格中不断变化的部分的布局边界,因此我不知道需要显示哪些“节点块”。

    有没有一个简单的方法来解决这个问题?还是我走错了路?(或两者)谢谢。

    0 回复  |  直到 5 年前
        1
  •  1
  •   Sai Dandem    5 年前

    注意:这不是你确切问题的答案。

    我不知道为什么要为每个图像创建ImageView。相反,我会为每个nodeChunk绘制一个图像(160000个图像),并为这个nodeChunk创建一个ImageView。

    在功能上,我看不出这两种方法之间有什么区别(您可以告诉我为什么要使用每个imageview)。但从性能上看,这种方法是非常快速和顺利的。

    请找到下面的演示我所说的。 enter image description here

    import javafx.application.Application;
    import javafx.embed.swing.SwingFXUtils;
    import javafx.scene.Group;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.control.ScrollPane;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    import javafx.scene.layout.GridPane;
    import javafx.stage.Stage;
    
        import java.awt.*;
        import java.awt.image.BufferedImage;
        import java.io.IOException;
        import java.security.SecureRandom;
        import java.util.HashMap;
        import java.util.Map;
    
        /**
         * Combining 4,000,000 images
         */
        public class ImageLoadingDemo extends Application {
    
            SecureRandom rnd = new SecureRandom();
            // I created 5 images of dimension 1 x 1  each with new color
            String[] images = {"1.png", "2.png", "3.png", "4.png", "5.png"};
            Map<Integer, BufferedImage> buffImages = new HashMap<>();
            Map<Integer, Image> fxImages = new HashMap<>();
    
            @Override
            public void start(Stage primaryStage) throws IOException {
                ScrollPane root = new ScrollPane();
                root.setPannable(true);
                Scene scene = new Scene(root, 800, 800);
                primaryStage.setScene(scene);
                primaryStage.show();
    
                root.setContent(approach1());  // Fast & smooth rendering
    
                // Your approach of creating ImageViews
                // root.setContent(approach2());  // Very very very very very slow rendering
            }
    
            private Node approach1(){
                // Creating 5 x 5 nodeChunk grid
                GridPane grid = new GridPane();
                for (int i = 0; i < 5; i++) {
                    for (int j = 0; j < 5; j++) {
                        ImageView nodeChunk = new ImageView(SwingFXUtils.toFXImage(createChunk(), null));
                        grid.add(nodeChunk, i, j);
                    }
                }
                return grid;
            }
    
            private BufferedImage createChunk() {
                // Combining 160000 1px images into a single image of 400 x 400
                BufferedImage chunck = new BufferedImage(400, 400, BufferedImage.TYPE_INT_ARGB);
                Graphics2D paint;
                paint = chunck.createGraphics();
                paint.setPaint(Color.WHITE);
                paint.fillRect(0, 0, chunck.getWidth(), chunck.getHeight());
                paint.setBackground(Color.WHITE);
                for (int i = 0; i < 400; i++) {
                    for (int j = 0; j < 400; j++) {
                        int index = rnd.nextInt(5); // Picking a random image
                        BufferedImage buffImage = buffImages.get(index);
                        if (buffImage == null) {
                            Image image = new Image(ImageLoadingDemo.class.getResourceAsStream(images[index]));
                            buffImages.put(index, SwingFXUtils.fromFXImage(image, null));
                        }
                        paint.drawImage(buffImage, i, j, null);
                    }
                }
                return chunck;
            }
    
            private Node approach2(){
                GridPane grid = new GridPane();
                for (int i = 0; i < 5; i++) {
                    for (int j = 0; j < 5; j++) {
                        grid.add(createGroup(), i, j);
                    }
                }
                return grid;
            }
            private Group createGroup() {
                GridPane grid = new GridPane();
                for (int i = 0; i < 400; i++) {
                    for (int j = 0; j < 400; j++) {
                        int index = rnd.nextInt(5);
                        Image fxImage = fxImages.get(index);
                        if (fxImage == null) {
                            fxImage = new Image(ImageLoadingDemo.class.getResourceAsStream(images[index]));
                            fxImages.put(index, fxImage);
                        }
                        grid.add(new ImageView(fxImage), i, j);
                    }
                }
                return new Group(grid);
            }
    
    
    
            public static void main(String[] args) {
                Application.launch(args);
            }
        }
    
        2
  •  0
  •   NotZack    5 年前

    最后,我为每个“节点块”创建了一个矩形,将不透明度设置为零,然后检查是否与用户看到的显示窗格部分发生冲突。当与矩形发生冲突时,相应的“节点块”将添加到显示窗格中。