在处理需要渲染精灵的Java应用程序时,我认为,不是将.png或.jpg文件作为Image
或BufferedImage
加载,我可以加载{{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个精灵在屏幕上渲染!请注意,BufferedImage
是g
对象。
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
加载到内存中真的没有问题吗?
BufferdImage
作为渲染所有内容的大型BufferedImage,然后渲染到g
。主要区别在于Canvas
在该方法中更改为g.fillRect(...
,但同样很慢。
编辑:我正在处理的图像是16x16和32x32像素。
答案 0 :(得分:0)
如果主要关注内存使用情况,我会BufferedImage
使用IndexColorModel
(TYPE_BYTE_BINARY
)。这将完美地反映您的byte[] image
和ColorPalette
,并且浪费很少的记忆。它们的绘制速度也相当快。
这种方法将使用初始使用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
构造函数,将最后一个参数设置为IndexColorModel
(true
):
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
)。