60

我在使用 Java2D 时遇到了性能异常。我知道 sun.java2d.opengl VM 参数可以为 2D 启用 3D 加速,但即使使用它也有一些奇怪的问题。

以下是我运行的测试结果:


在 JComponent Image 1 = .bmp 格式,Image 2 = A .png 格式上绘制具有 32x32 像素图块的 25x18 地图

没有 -Dsun.java2d.opengl=true

120 FPS 使用 .BMP 图像 1
13 FPS 使用 .PNG 图像 2

使用 -Dsun.java2d.opengl=true

12 FPS 使用 .BMP 图像 1
700 FPS 使用 .PNG 图像 2

如果没有加速,我假设我在软件中执行的每个 drawImage() 都会发生某种转换,并且在 .PNG 的情况下会大大降低 FPS。但是,为什么随着加速,结果会发生变化(而且 PNG 实际上执行得更快)?!疯狂!

.BMP 图像 1 被转换为 TYPE_INT_RGB 的图像类型。.PNG 图像 2 被转换为 TYPE_CUSTOM 的图像类型。为了在使用和不使用 opengl 加速的情况下获得一致的速度,我必须创建一个图像类型为 TYPE_INT_ARGB 的新 BufferedImage,并将图像 1 或图像 2 绘制到这个新图像上。

以下是运行的结果:

没有 -Dsun.java2d.opengl=true

120 FPS 使用 .BMP 图像 1
120 FPS 使用 .PNG 图像 2

使用 -Dsun.java2d.opengl=true

700 FPS 使用 .BMP 图像 1
700 FPS 使用 .PNG 图像 2

我真正的问题是,我可以假设 TYPE_INT_ARGB 将是所有系统和平台的本机图像类型吗?我假设这个值可能不同。有没有办法让我获得本机值,以便我总是可以创建新的 BufferedImages 以获得最佳性能?

提前致谢...

4

3 回答 3

69

我想我通过研究和整理来自太多 Google 搜索的点点滴滴找到了解决方案。

在这里,评论和所有:

private BufferedImage toCompatibleImage(BufferedImage image)
{
    // obtain the current system graphical settings
    GraphicsConfiguration gfxConfig = GraphicsEnvironment.
        getLocalGraphicsEnvironment().getDefaultScreenDevice().
        getDefaultConfiguration();

    /*
     * if image is already compatible and optimized for current system 
     * settings, simply return it
     */
    if (image.getColorModel().equals(gfxConfig.getColorModel()))
        return image;

    // image is not optimized, so create a new image that is
    BufferedImage newImage = gfxConfig.createCompatibleImage(
            image.getWidth(), image.getHeight(), image.getTransparency());

    // get the graphics context of the new image to draw the old image on
    Graphics2D g2d = newImage.createGraphics();

    // actually draw the image and dispose of context no longer needed
    g2d.drawImage(image, 0, 0, null);
    g2d.dispose();

    // return the new optimized image
    return newImage; 
}

在我之前的帖子中,GraphicsConfiguration 保存了在系统上创建优化图像所需的信息。它似乎工作得很好,但我原以为 Java 会自动为你做这件事。显然你不能对 Java 感到太舒服。:) 我想我最终回答了我自己的问题。哦,好吧,希望它能帮助你们中的一些人,我见过尝试将 Java 用于 2D 游戏。

于 2008-10-13T09:04:29.147 回答
5

好吧,这是一篇旧文章,但我想分享一下我对使用 Swing/AWT 直接绘图的发现,而无需使用 BufferedImage。

直接绘制到int[]缓冲区时,某些类型的绘图(如 3D)会更好。完成图像后,您可以使用ImageProducer实例(如MemoryImageSource)来生成图像。我假设您知道如何直接执行绘图,而无需 Graphics/Graphics2 的帮助。

    /**
* How to use MemoryImageSource to render images on JPanel
* Example by A.Borges (2015)
*/
public class MyCanvas extends JPanel implements Runnable {

public int pixel[];
public int width;
public int height;
private Image imageBuffer;   
private MemoryImageSource mImageProducer;   
private ColorModel cm;    
private Thread thread;


public MyCanvas() {
    super(true);
    thread = new Thread(this, "MyCanvas Thread");
}

/**
 * Call it after been visible and after resizes.
 */
public void init(){        
    cm = getCompatibleColorModel();
    width = getWidth();
    height = getHeight();
    int screenSize = width * height;
    if(pixel == null || pixel.length < screenSize){
        pixel = new int[screenSize];
    }        
    mImageProducer =  new MemoryImageSource(width, height, cm, pixel,0, width);
    mImageProducer.setAnimated(true);
    mImageProducer.setFullBufferUpdates(true);  
    imageBuffer = Toolkit.getDefaultToolkit().createImage(mImageProducer);        
    if(thread.isInterrupted() || !thread.isAlive()){
        thread.start();
    }
}
/**
* Do your draws in here !!
* pixel is your canvas!
*/
public /* abstract */ void render(){
    // rubisch draw
    int[] p = pixel; // this avoid crash when resizing
    if(p.length != width * height) return;        
    for(int x=0; x < width; x++){
        for(int y=0; y<height; y++){
            int color =  (((x + i) % 255) & 0xFF) << 16; //red
                color |= (((y + j) % 255) & 0xFF) <<  8; //green
                color |= (((y/2 + x/2 - j) % 255) & 0xFF) ;   //blue         
            p[ x + y * width] = color;
        }
    }        
    i += 1;
    j += 1;          
}    
private int i=1,j=256;

@Override
public void run() {
    while (true) {
        // request a JPanel re-drawing
        repaint();                                  
        try {Thread.sleep(5);} catch (InterruptedException e) {}
    }
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    // perform draws on pixels
    render();
    // ask ImageProducer to update image
    mImageProducer.newPixels();            
    // draw it on panel          
    g.drawImage(this.imageBuffer, 0, 0, this);  
}

/**
 * Overrides ImageObserver.imageUpdate.
 * Always return true, assuming that imageBuffer is ready to go when called
 */
@Override
public boolean imageUpdate(Image image, int a, int b, int c, int d, int e) {
    return true;
}
}// end class

请注意,我们需要MemoryImageSourceImage的唯一实例。不要为每个帧创建新的 Image 或新的 ImageProducer,除非您调整了 JPanel 的大小。请参见上面的init()方法。

在渲染线程中,询问repaint()。在 Swing 上,repaint()将调用被覆盖的paintComponent(),它会调用您的render()方法,然后要求您的 imageProducer 更新图像。完成 Image 后,使用Graphics.drawImage()绘制它。

要获得兼容的 Image,请在创建Image时使用正确的ColorModel。我使用GraphicsConfiguration.getColorModel()

/**
 * Get Best Color model available for current screen.
 * @return color model
 */
protected static ColorModel getCompatibleColorModel(){        
    GraphicsConfiguration gfx_config = GraphicsEnvironment.
            getLocalGraphicsEnvironment().getDefaultScreenDevice().
            getDefaultConfiguration();        
    return gfx_config.getColorModel();
}
于 2016-02-10T04:17:58.793 回答
2

我记得当我考虑用 Java 进行图形编程时,内置库很慢。我在 GameDev.Net 上被告知,任何做严肃事情的人都必须使用类似jogl的东西

于 2008-10-13T07:01:43.793 回答