2013-09-22 40 views
13

在Java中进行2D游戏开发时,大多数教程都会创建一个缓冲策略来渲染。这非常合理。 但是,人们似乎倾向于将实际图形绘制到缓冲区的方法。Java缓冲策略图形或整型数组

一些教程创建了一个缓冲图像,然后创建一个整数数组来表示各个像素的颜色。

private BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); 

Graphics g = bs.getDrawGraphics(); 
g.setColor(new Color(0x556B2F)); 
g.fillRect(0, 0, getWidth(), getHeight()); 
g.drawImage(image, 0, 0, getWidth(), getHeight(), null); 

然而,一些其他教程不创建缓冲图像,绘图的像素为int阵列,而是使用BufferStrategy中的图形部件直接绘制它们的图像的缓冲器。

Graphics g = bs.getDrawGraphics(); 
g.setColor(new Color(0x556B2F)); 
g.fillRect(0, 0, getWidth(), getHeight()); 

g.drawImage(testImage.image, x*128, y*128, 128, 128, null); 

我只是想知道,为什么要创建整个int数组,然后绘制它。这需要在实现矩形,拉伸,透明度等方面做更多的工作。缓冲策略的图形组件已经具有可以很容易地调用的方法。 使用int数组会有一些巨大的性能提升吗?

我已经看了几个小时,所有我见过的网站只是解释他们在做什么,而不是他们为什么选择这样做。

+1

我不是100%确定,但我会出现一些人认为它更快地更新int /像素数组,然后使用图形API。这可能是真实的,人们也可能不知道如何创建兼容的图形对象。使用缓冲策略应该提供几乎直接访问硬件层(如果可用),所以我不明白为什么你需要明白为什么你需要使用一个int数组,我只是这样,而我懒得那样;) – MadProgrammer

+0

是的,我正在用这两种方法进行测试。使用int数组,我可以在我的fps开始减少到120以下之前改变大约27648000像素。使用图形对象,我可以渲染透明图像,放大到几千倍的缩放矩形,这几乎与int数组相等。 使用图形对象似乎更有用。 – Kabistarz

+0

前段时间,我做了一个比较简单的例子,只用了一些JPanel和一些自定义绘画作为主要对象。然后我得到了4500个这些物体都朝不同的方向移动,包括主要物体的旋转,因此它指向它的运动方向。不确定帧速率是多少,但是我以25fps的速度刷新了它,并且它的表现非常出色。我认为在渲染管道中有很多优化,可以使用DirectX或OpenGL,但需要进行实验;) – MadProgrammer

回答

5

让我们清楚一件事:代码片段做同样的事情 - 绘制图像。然而,片段并不完整 - 第二个片段没有显示'testImage.image'实际是什么或者它是如何创建的。但是他们最终都调用Graphics.drawImage()和drawImage()的所有变体在Graphics或Graphics2D中绘制一个Image,很简单。在第二种情况下,我们根本不知道它是否是一个BufferedImage,一个VolatileImage,甚至是一个Toolkit Image。

所以在这里实际绘制的图画没有区别!

这两个片段之间只有一个区别 - 第一个获得最终内部支持Image实例的整数数组的直接引用。这使得可以直接访问像素数据,而不必通过(缓冲)Image API使用相对较慢的getRGB()和setRGB()方法。为什么这样做不能在上下文中具体化,在这个问题中,数组是获得的,但永远不会在代码片段中使用。因此,为了给出下面的解释存在的任何理由,我们必须假定有人想要直接读取或编辑图像的像素,很可能出于优化原因,给出(缓冲)图像API的“缓慢”操纵数据。

而那些优化的原因可能是一个过早的优化,可能会适得其反。


首先,这段代码只工作,因为图像的类型是INT_RGB,它会给图像一个IntDataBuffer。如果它是另一种类型的图像,例如3BYTE_BGR,则此代码将因ClassCastException而失败,因为后备数据缓冲区不会是IntDataBuffer。当您只手动创建图像并强制执行特定类型时,这可能不是什么大问题,但图像往往会从外部工具创建的文件中加载。其次,直接访问像素缓冲区还有另一个更大的缺点:当你这样做时,Java2D将拒绝加速该图像,因为它无法知道你何时将其更改为在其控制之外进行更改。为了清楚起见,加速是在视频内存中保留未改变的图像的过程,而不是在每次绘制时从系统内存中复制它。这可能是一个巨大的性能改进(或者如果您打破它,则会有所损失),具体取决于您使用的图像数量。

How can I create a hardware-accelerated image with Java2D?

(作为该相关问题表明你:你应该使用GraphicsConfiguration.createCompatibleImage()来构造BufferedImage的实例)。

所以实质上:尝试对所有东西都使用Java2D API,不要直接访问缓冲区。这种场外资源提供了一个好主意,正是采用了API已经支持您在不必再低的水平:

所有的

http://www.pushing-pixels.org/2008/06/06/effective-java2d.html

5

首先,有很多历史方面。早期的API是非常基本的,所以做任何不平凡的唯一方法就是实现所有需要的基元。

原始数据访问有点过时,我们可以尝试做一些“考古学”来找出使用这种方法的原因。我认为有两个主要原因:

1滤镜效果

让我们不要忘记滤镜效果(各种模糊的,等等)是简单的,非常为任何游戏开发商重要和广泛的应用。

enter image description here

的simples方法来实现与Java 1这样的效果是使用int阵列和过滤器定义为一个矩阵。赫伯特·希,例如,曾经有很多这样的演示:

public class Blur { 

    public void convolve() { 
     for (int y = 1; y < height - 1; y++) { 
      for (int x = 1; x < width - 1; x++) { 
       int rs = 0; 
       int gs = 0; 
       int bs = 0; 
       for (int k = -1; k <= 1; k++) { 
        for (int j = -1; j <= 1; j++) { 
         int rgb = imgpixels[(y + k) * width + x + j]; 
         int r = (rgb >> 16) & 0xff; 
         int g = (rgb >> 8) & 0xff; 
         int b = rgb & 0xff; 
         rs += r; 
         gs += g; 
         bs += b; 
        } 
       } 
       rs /= 9; 
       gs /= 9; 
       bs /= 9; 
       newimgpixels[y * width + x] = (0xff000000 
         | rs << 16 | gs << 8 | bs); 
      } 
     } 
    } 
} 

当然,你可以实现使用getRGB,但原始数据的访问方式更加有效。后来,Graphics2D提供更好的抽象层:

公共接口BufferedImageOp中

此接口描述 对BufferedImage 对象执行的单输入/单输出操作。它由AffineTransformOp,ConvolveOp, ColorConvertOp,RescaleOp和LookupOp实现。这些对象可以通过 传递到BufferedImageFilter中,以在ImageProducer-ImageFilter-ImageConsumer范例中的BufferedImage上操作。

2.双缓冲

另一个问题是有关闪烁和很慢图纸。双缓冲消除了丑陋的闪烁,突然它提供了一个简单的方法来做滤波效果,因为你已经有了缓冲区。

enter image description here

就像一个最终的结论:)

我会说你所描述的情况是很常见的任何发展中的技术。有两种方法可以实现同样的目标:

  • 使用传统的方法,代码比较多,等
  • 依靠新的抽象层,提供技术等

也有一些有用的扩展,简化你的生活更多,所以没有必要使用int[] :)

+0

谢谢,不需要那样做。这很有趣。而且,看起来好像我有足够的观点;) –