【问题标题】:Java- paintComponent most efficient way of drawing millions of squares from an arrayJava-paintComponent 从数组中绘制数百万个正方形的最有效方法
【发布时间】:2015-09-30 06:44:36
【问题描述】:

我根据一些示例编写了一个程序来模拟康威的生活游戏。 游戏中的每个单元格要么活着,要么死亡,它们存储在一个整数数组int[][] 这个数组中始终至少为 1000*1000,这意味着有数百万个单元格。

遍历数组以查找应用所有不同的规则很好,并且不是特别占用 CPU。但是,用于在 JFrame 上绘制这些矩形的方法可能会导致我的 Xeon E3 1246 V3 (I7 4770) 上的 CPU 使用率超过 90%。在 2000^2 的数组大小上,它可以导致 Windows 10 完全硬锁定。

@Override
    protected void paintComponent(Graphics g) {
        liveCells = 0;
        super.paintComponent(g);
        Color gColor = g.getColor();
        if(!backgroundWork || isPaused) {
            for (int row = 0; row < grid.length; row++) {
                for (int column = 0; column < grid[row].length; column++) {
                    //Checking if the cell is alive.
                    if (grid[row][column] == 1 ) {
                        liveCells++;
                        g.setColor(Color.red);
                        g.fillRect(column * UIScale, row * UIScale, UIScale, UIScale);

                    }
                }
            }
        }

        g.setColor(gColor);
        if (isPaused) { 
            g.drawString("The game is paused", 0, 30);
            g.drawString("Generation: " + generationCounter, 0, 10);
            g.drawString("Living cells: " + liveCells, 150, 10);
        } else { //If the game is running
            g.drawString("Generation: " + generationCounter++, 0, 10);
            g.drawString("Living cells: " + liveCells, 100, 10);
        }
        g.setColor(gColor);
        try {
            /* Sleep for some microseconds. */
            TimeUnit.MICROSECONDS.sleep(sleepTimer);
        } catch (InterruptedException ex) {
            System.err.println("An InterruptedException was caught: " + ex.getMessage());
        }
    }

我可以很清楚地看到,单元格的绘制是问题所在,为了让程序运行得更快,我已经添加了一个更改变量 backgroundWork 的方法,它禁用了更新矩形网格。打开或关闭此功能会导致任务管理器 CPU 利用率的差异高达 80%。

使用当前绘制网格的方法,我看不出有什么方法可以让它更快,因为所有的单元格都是相互独立的,一般不会超过 3 个旁边是红色的反正彼此,所以没有理由实现一种同时绘制多个的方法。

谁能提出一种加快当前进程的方法,或者另一种绘制正方形的方法。 感谢您的帮助。

【问题讨论】:

  • 您是否考虑过直接翻转 BufferedImage 的 Raster 中的位,从 EDT 中执行此操作,然后在您的 paintComponent 方法中绘制图像?
  • 我必须对其进行调整以允许不同大小的单元格。目前 g.fillRectangle 绘制一个边长为 UIScale 的正方形。根据设置的内容,它可以是数组中的值与屏幕上的像素之间的 1 比 1 表示,或者数组中的一项为 4*4 像素。
  • 我不确定创建与网格大小相同的缓冲图像是否更有意义,然后在屏幕上将其放大,或者允许图像中的多个像素表示一个细胞。
  • 您可以通过将int 的网格更改为boolean 的网格来节省大量内存。

标签: java arrays performance swing


【解决方案1】:

Hovercraft Full Of Eels(顺便提一下,名字不错)建议的这种方法比我以前使用的要快得多-

@Override
protected void paintComponent(Graphics g) {
    liveCells = 0;

    BufferedImage BI = new BufferedImage(UIDimensions, UIDimensions, BufferedImage.TYPE_INT_RGB);

    super.paintComponent(g);
    Color gColor = g.getColor();
    if(!backgroundWork || isPaused) {
        for (int row = 0; row < grid.length; row++) {
            for (int column = 0; column < grid[row].length; column++) {
                //Checking if the cell is alive.
                if (grid[row][column] == 1 ) {
                    liveCells++;
                    if(UIScale != 1) {
                        //TODO- Draw squares larger than one pixel
                    } else {
                        BI.setRGB(column, row, 16711680);
                    } 

                    //The old code is commented out below

                    //g.setColor(Color.red);
                    //Drawing the colour in a 4x4 pixel square. With a window of 1000x1000, there are 250x250 organisms, hence the /4 everywhere
                    //g.fillRect(column * UIScale, row * UIScale, UIScale, UIScale);
                    //The way that this works is that it draws rectangles at the coordinates of the grid.
                    //The graphics on the screen aren't a grid themselves, they are just lots of squares
                } else {
                    BI.setRGB(column, row, 16777215);
                }
            }
        }
        g.drawImage(BI, 0, 0, null);
    }

    g.setColor(gColor);

    if (isPaused) { //If the game is paused (isPaused is true)
        g.drawString("The game is paused", 0, 30);
        g.drawString("Generation: " + generationCounter, 0, 10);
        g.drawString("Living cells: " + liveCells, 150, 10);
    } else { //If the game is running
        g.drawString("Generation: " + generationCounter++, 0, 10);
        g.drawString("Living cells: " + liveCells, 100, 10);
    }
    g.setColor(gColor);
    try {
        /* Sleep for some seconds. */
        TimeUnit.MICROSECONDS.sleep(sleepTimer);
    } catch (InterruptedException ex) {
        System.err.println("An InterruptedException was caught: " + ex.getMessage());
    }
}

【讨论】:

  • 我不建议您在 Swing 事件线程中调用 sleep ,这在 paintComponent 方法中是双重的。我很惊讶这样做并没有让你的整个应用进入睡眠状态。
  • 补充说明: 1. 真的 不要在sleep那里这样做! 2.不要每次都创建一个新的BufferedImage。您可能应该只创建一次(即,当您创建 grid 时)。 3.为了“绘制大于一个像素的平方”,您可以在drawImage调用之前简单地做((Graphics2D)g).scale(uiScale,uiScale);。 4. 不要使用这些十进制常量。相反,使用十六进制:0xFF000000 是不透明的黑色,0xFFFF0000 是不透明的红色,0xFF00FF00=green,0xFF0000FF=blue 等等。
猜你喜欢
  • 2011-06-18
  • 2015-09-01
  • 2016-04-26
  • 2014-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-02
  • 2023-03-22
相关资源
最近更新 更多