Java的g.drawImage在Windows上比Linux需要更长的时间

时间:2014-05-07 03:25:32

标签: java linux windows swing drawimage

对于一个课程项目,我和其他一些人正在研究等距游戏。使用缓冲图像在一个JPanel中绘制所有内容。每件艺术品都在烟花中完成并保存为.png。

在Linux上,每个游戏刻度重绘地图所需的时间约为3毫秒。在Windows(以及OSx)上,它大约需要100ms才能达到500ms。

在4台不同的计算机上观察到此效果,从典型的笔记本电脑到i7-3770K + 660游戏机。发生这种情况时的CPU使用率约为10-20%,程序的RAM使用率约为1GB。这个问题已在很多地方在互联网上进行了研究,但是我们的部门负责人(负责该项目的人)也很难过。有什么想法吗?

这是JPanel中的paint组件和setEntityImage方法。您可以在paintComponent的底部看到时间戳的位置。 setEntityImage每500ms设置一个新的BufferedImage,它在传递到这个JPanel之前在另一个线程上预先绘制。我包括那些代码,因为我很好奇,如果这是一个可能的线程问题。

此代码每隔500毫秒调用一次,由“游戏勾号”组成,它绘制图像(这些图像都预先加载在静态单例类中)。

    BufferedImage entityImg = ImageUtil.getFromGraphics(map.getWidth() * TILE_WIDTH
            + TILE_WIDTH, map.getHeight() * TILE_HEIGHT + TILE_HEIGHT);

    Graphics2D g2 = (Graphics2D) entityImg.getGraphics();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

    for (int i = 0; i < map.getHeight(); i++)
    {
        for (int j = 0; j < map.getWidth(); j++)
        {
            try
            {
                int x, y;
                x = (j * TILE_WIDTH_HALF - i * TILE_WIDTH_HALF)
                        + map.getWidth() * TILE_WIDTH_HALF;
                y = j * TILE_HEIGHT_HALF + i * TILE_HEIGHT_HALF;

                Entity e = map.getTile(i, j).getEntity();

                if (e != null)
                {
                    g2.drawImage(e.getImage(), x, y, 32, 32, null);
                }
            }
            catch (Exception e)
            {
            }
        }
    }

    panel.setEntityImage(entityImg);

此代码位于我们的MapPanel中,它扩展了JPanel。

public void setEntityImage(BufferedImage entityImg)
{
    this.entityImg = entityImg;
}

@Override
public void paintComponent(Graphics g)
{
    super.paintComponent(g);

    Graphics g2 = (Graphics2D) g;

    g2.fillRect(0, 0, tileImg.getWidth(), tileImg.getHeight());

    // Draw tiles
    g2.drawImage(tileImg, 0, 0, null);

    // Draw box over square mouse is hovering over
    if (hover)
    {
        g2.setColor(new Color(255, 255, 255, 128));
        int screenX = (this.x * 16 - this.y * 16) + Window.WIDTH * 16;
        int screenY = (this.x * 8 + this.y * 8) + 16;

        g2.drawLine(screenX, screenY + 8, screenX + 16, screenY);
        g2.drawLine(screenX + 16, screenY, screenX + 32, screenY + 8);
        g2.drawLine(screenX, screenY + 8, screenX + 16, screenY + 16);
        g2.drawLine(screenX + 16, screenY + 16, screenX + 32, screenY + 8);
    }

    long time = System.currentTimeMillis();
    g2.drawImage(entityImg, 0, 0, null);
    System.out.println(System.currentTimeMillis() - time);
}

1 个答案:

答案 0 :(得分:0)

有一些内部优化缓存,我不知道的细节。基本上,如果BufferedImage没有更新,它会在0毫秒内绘制。

我做了两次优化,不确定是否单独工作但是它在性能上有所不同,但不是在时间延迟(即,当我拖动地图时,它明显更好)

第一次是Window#tickUpdate。而是每500毫秒创建一个新的BufferedImage,这只是重新使用单个BufferedImage,如果没有先前的实例或者大小已经更改,则只创建一个新实例...

public void tickUpdate(Map map) {
    int width = map.getWidth() * TILE_WIDTH + TILE_WIDTH;
    int height = map.getHeight() * TILE_HEIGHT + TILE_HEIGHT;
    if (entityImg == null || entityImg.getWidth() != width || entityImg.getHeight() != height) {
        entityImg = ImageUtil.getFromGraphics(width, height);
    }

    Graphics2D g2 = (Graphics2D) entityImg.getGraphics();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
    g2.setBackground(new Color(255, 255, 255, 0));
    g2.clearRect(0, 0, width, height);
    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));

    for (int i = 0; i < map.getHeight(); i++) {
        for (int j = 0; j < map.getWidth(); j++) {

            Entity e = map.getTile(i, j).getEntity();

            if (e != null) {
                int x, y;
                x = (j * TILE_WIDTH_HALF - i * TILE_WIDTH_HALF)
                        + map.getWidth() * TILE_WIDTH_HALF;
                y = j * TILE_HEIGHT_HALF + i * TILE_HEIGHT_HALF;
                g2.drawImage(e.getImage(), x, y, 32, 32, null);
            }
        }
    }
    g2.dispose();

    panel.setEntityImage(entityImg);
    panel.repaint();
}

因为BufferedImage可以在绘画时更新,在MapPanel中,我创建了另一个BufferedImage,其上绘制了“实体”图像。

public void setEntityImage(BufferedImage img) {
    entityLock.lock();
    try {
        if (entityImg == null || entityImg.getWidth() != img.getWidth() || entityImg.getHeight() != img.getHeight()) {
            entityImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
        }
        Graphics2D g2d = entityImg.createGraphics();
        g2d.drawImage(img, 0, 0, null);
        g2d.dispose();
    } finally {
        entityLock.unlock();
    }
}

entitytLock只是ReentrantLock我用来阻止paintComponent方法在更新时尝试绘制BufferedImage

这不会改变时间,但会在视觉上做得更好......

另一种方法是使用VolatileImage ...