使用调色板快速将字节渲染到画布

时间:2015-10-16 12:18:37

标签: java performance canvas graphics bufferedimage

在处理需要渲染精灵的Java应用程序时,我认为,不是将.png或.jpg文件作为ImageBufferedImage加载,我可以加载{{1包含调色板索引的数组(每个调色板16个颜色,每个byte[]两个像素),然后渲染。

我目前在初始化时从byte数组和调色板生成BufferedImage,花费额外的时间进行初始化,但之后运行顺利,效果很好,但只有4个精灵到目前为止在该计划中。我担心当有超过100个精灵时,将它们全部存储为byte[]会对内存造成太大的负担。而且这不仅意味着每个精灵1 BufferedImages,而且实际上我想要使用的每个精灵/调色板组合都有1个图像。

此函数创建BufferedImage:

BufferedImage

这个实际上将它渲染到屏幕上:

protected BufferedImage genImage(ColorPalette cp, int width, int height){ //Function to generate BufferedImage to render from the byte[]
    BufferedImage ret = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); //Create the Image to return
    for(int j=0; j<height; j++){ //Run a for loop for each pixel
        for(int i=0; i<width; i++){
            int index = (j * width + i)/2; //Get the index of the needed byte
            int value = image[index] & 0x00ff; //Convert to "unsigned byte", or int
            byte thing; //declare actual color index as byte
            if(i % 2 == 0)thing = (byte)((value & 0b11110000) >>> 4); //If it's an even index(since it starts with 0, this includes the 1st one), get the first 4 bits of the value
            else thing = (byte)(value & 0b00001111); //If it's odd, get the last four bits
            ret.setRGB(i, j, cp.getColor(thing & 0x00ff).getRGB()); //Set the pixel in the image to the value in the Color Palette
        }
    }
    return ret;
}

我已经尝试了另一种方法,即从public void render(Graphics g, int x, int y){ //Graphics to render to and x/y coords g.drawImage(texture, x, y, TILE_WIDTH, TILE_HEIGHT, null); //Render it } 直接呈现byte[]的需要,理论上应该通过避免使用BufferedImage来节省内存对于每个精灵,但它最终变得非常非常慢。花了几秒钟渲染每个帧,最多25个精灵在屏幕上渲染!请注意,BufferedImageg对象。

Graphics

那么有没有更有效的方法来呈现这些private void drawSquare(int x, int y, int scale, Color c){ //Draw each "pixel" to scale if(g == null){ //If null, quit return; } g.setColor(c); //Set the color for(int i=x; i<x+scale; i++){ //Loop through each pixel if(i<0)continue; for(int j=y; j<y+scale; j++){ if(j<0)continue; g.fillRect(x, y, scale, scale); //Fill the rect to make the "pixel" } } } public void drawBytes(byte[] image, int x, int y, int width, int height, int scale, ColorPalette palette){ //Draw a byte[] image with given byte[], x/y coords, width/height, scale, and color palette if(image.length < width * height / 2){ //If the image is too small, exit return; } for(int j=0; j<height; j++){ //Loop through each pixel for(int i=0; i<width; i++){ int index = (j * width + i)/2; //Get index int value = image[index]; //get the byte byte thing; //get the high or low value depending on even/odd if(i % 2 == 0)thing = (byte)((value & 0b11110000) >>> 4); else thing = (byte)(value & 0b00001111); drawSquare((int)(x + scale * i), (int)(y + scale * j), scale, palette.getColor(thing)); //draw the pixel } } } 数组而不需要byte[]?或者将几百个BufferedImage加载到内存中真的没有问题吗?

编辑:我也尝试过使用no-BufferedImage方法,但使用BufferdImage作为渲染所有内容的大型BufferedImage,然后渲染到g。主要区别在于Canvas在该方法中更改为g.fillRect(...,但同样很慢。

编辑:我正在处理的图像是16x16和32x32像素。

1 个答案:

答案 0 :(得分:0)

如果主要关注内存使用情况,我会BufferedImage使用IndexColorModelTYPE_BYTE_BINARY)。这将完美地反映您的byte[] imageColorPalette,并且浪费很少的记忆。它们的绘制速度也相当快。

这种方法将使用初始使用TYPE_INT_RGB BufferedImage时使用的内存的大约1/8,因为我们保留每像素4位而不是32位({{1}每个像素是32位)(当然还有调色板的一些开销)。

int

上面的代码将创建完全不透明的(public static void main(String[] args) { byte[] palette = new byte[16 * 3]; // 16 color palette without alpha byte[] pixels = new byte[(16 * 16 * 4) / 8]; // 16 * 16 * 4 bit Random random = new Random(); // For test purposes, just fill arrays with random data random.nextBytes(palette); random.nextBytes(pixels); // Create ColorModel & Raster from palette and pixels IndexColorModel cm = new IndexColorModel(4, 16, palette, 0, false, -1); // -1 for no transparency DataBufferByte buffer = new DataBufferByte(pixels, pixels.length); WritableRaster raster = Raster.createPackedRaster(buffer, 16, 16, 4, null); // Create BufferedImage from CM and Raster final BufferedImage image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null); System.out.println("image: " + image); // "image: BufferedImage@...: type = 12 ..." SwingUtilities.invokeLater(new Runnable() { @Override public void run() { JFrame frame = new JFrame("Foo"); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.add(new JLabel(new ImageIcon(image))); frame.pack(); frame.setVisible(true); } }); } )图像,这将占据整个16 x 16像素块。

如果您想要位掩码(Transparency.OPAQUE)透明度,其中所有像素都是完全不透明或透明的,只需将Transparency.BITMASK中的最后一个参数更改为您想要完全透明的调色板索引。

IndexColorModel

这样可以让你的精灵拥有你想要的任何形状。

如果你想要半透明像素(int transparentIndex = ...; IndexColorModel cm = new IndexColorModel(4, 16, palette, 0, false, transparentIndex); // ...everything else as above ),像素可以是半透明的,你也可以拥有它。然后,您必须将Transparency.TRANSLUCENT数组更改为palette条目,并将alpha值的样本包含为每个条目的第4个样本(四倍)。然后调用16 * 4构造函数,将最后一个参数设置为IndexColorModeltrue):

hasAlpha

这将使精灵的透明和非透明部分之间的渐变更平滑。但是在调色板中只有16种颜色,您将无法获得许多可用于透明度的条目。

请注意,在上述所有示例中,可以重复使用此处的byte[] palette = new byte[16 * 4]; // 16 color palette with alpha (translucency) // ... IndexColorModel cm = new IndexColorModel(4, 16, palette, 0, true); // true for palette with alpha samples // ...everything else as above Raster来为使用相同调色板的图像保存更多内存,甚至使用图像具有不同调色板的相同图像数据。但是有一点需要注意,那就是共享栅格的图像将是&#34;直播视图&#34;彼此相关,所以如果你对其中一个进行任何更改,你将全部更改。但如果您的图像永远不会改变,您可以利用这一事实。

尽管如此,上述确实是在节省记忆和“合理”之间的妥协。性能。如果性能(即每秒帧数)更重要,只需忽略内存使用情况,并创建与您的图形卡/操作系统的本机像素布局兼容的IndexColorModel。您可以使用BufferedImage(其中component.createCompatibleImage(...)component子类)或JComponent(其中gfxConfig.createCompatibleImage(...)是从gfxConfig获得的GraphicsConfiguration来执行此操作本地GraphicsEnvironment)。