Java bufferstrategy图形或整数数组

Kab*_*arz 13 java graphics integer render bufferstrategy

在Java中进行2D游戏开发时,大多数教程都会创建一个缓冲区来进行渲染.这很有道理.然而,人们似乎倾斜的是将实际图形绘制到缓冲区的方法.

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

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);
Run Code Online (Sandbox Code Playgroud)

但是,其他一些教程不会创建缓冲图像,将像素绘制到int数组,而是使用BufferStrategy的Graphics组件将其图像直接绘制到缓冲区.

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);
Run Code Online (Sandbox Code Playgroud)

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

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

Gim*_*mby 5

让我们清楚一件事:两个代码片段完全相同 - 绘制一个图像.但是片段相当不完整 - 第二个片段没有显示'testImage.image'实际上是什么或者是如何创建的.但它们最终都调用了Graphics.drawImage(),并且Graphics或Graphics2D中的所有drawImage()变体都绘制了一个简单明了的Image.在第二种情况下,我们根本不知道它是BufferedImage,VolatileImage还是Toolkit Image.

所以这里的插图没有区别!

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

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


首先,这段代码只能起作用,因为图像的类型是INT_RGB,它会给图像一个IntDataBuffer.如果它是另一种类型的图像,例如ex 3BYTE_BGR,则此代码将因ClassCastException而失败,因为后备数据缓冲区将不是IntDataBuffer.当您仅手动创建图像并强制执行特定类型时,这可能不是什么大问题,但图像往往是从外部工具创建的文件加载的.

其次,直接访问像素缓冲区还有另一个更大的缺点:当你这样做时,Java2D将拒绝加速该图像,因为它无法知道你何时会在其控制之外对其进行更改.仅为了清楚起见:加速是在视频存储器中保持未改变的图像而不是在每次绘制时将其从系统存储器中复制的过程.这可能会带来巨大的性能提升(或者如果你打破它会丢失),具体取决于你使用的图像数量.

如何使用Java2D创建硬件加速映像?

(正如相关问题所示:您应该使用GraphicsConfiguration.createCompatibleImage()来构造BufferedImage实例).

所以本质上:尝试将Java2D API用于所有内容,不要直接访问缓冲区.这个非现场资源可以很好地了解API必须支持哪些功能,而不必进入低级别:

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


Ren*_*nov 5

首先,有很多历史方面.早期的API非常基础,所以做任何非平凡事情的唯一方法就是实现所有必需的原语.

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

1.过滤效果

让我们不要忘记过滤效果(各种模糊等)很简单,对于任何游戏开发者都非常重要并且被广泛使用.

在此输入图像描述

使用Java 1实现此类效果的简单方法是使用int定义为矩阵的数组和过滤器.例如,Herbert Schildt曾经有很多这样的演示:

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);
            }
        }
    }
} 
Run Code Online (Sandbox Code Playgroud)

当然,您可以使用getRGB,但原始数据访问更有效.后来,Graphics2D提供了更好的抽象层:

公共接口BufferedImageOp

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

2.双缓冲

另一个问题与闪烁和绘图速度很慢有关.双缓冲消除了丑陋的闪烁,突然间它提供了一种简单的方法来进行过滤效果,因为你已经有了缓冲区.

在此输入图像描述

像最后的结论:)

我想说你所描述的情况对于任何不断发展的技术来说都很常见.有两种方法可以实现相同的目标:

  • 使用遗留方法,更多代码等
  • 依赖新的抽象层,提供技术等

还有一些有用的扩展可以简化你的生活,所以不需要使用int[]:)