2015-10-16 70 views
0

在处理需要渲染精灵的Java应用程序时,我认为,不是将.png或.jpg文件加载为ImageBufferedImage,我可以加载byte[]阵列包含调色板的索引(每个调色板16个颜色,因此每个byte有两个像素),然后渲染该索引。使用调色板快速将字节渲染到画布

我现在有该方法从byte[]阵列和彩色调色板生成BufferedImage而初始化,考虑额外的时间来初始化但在这之后,它工作正常运行平稳,但也有只有4精灵在程序为止。我担心,如果有100多个精灵,将它们全部存储为BufferedImages将对存储器造成太大的影响。这不仅意味着每个精灵1 BufferedImage,而且实际上每个精灵/调色板组合的1个图像我都想使用。

这个函数创建的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[]直接W/O呈现的另一种方法需要一个BufferedImage,理论上这应该通过避免使用每个精灵的BufferedImage来节省内存,但最终会非常缓慢。需要花费几秒钟的时间才能渲染每帧至多25个精灵来渲染屏幕!请注意0​​是一个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[]阵列W/O需要BufferedImage的更有效的方法?或者将数百个BufferdImage加载到内存中真的没有问题?

编辑:我也试着做无BufferedImage的方法,但作为g一个大的BufferedImage到一切都被渲染,然后呈现给Canvas。主要区别在于g.fillRect(...在该方法中更改为g.setRGB(...,但它同样缓慢。

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

+0

除了栅格数据(字节数组)之外,BufferedImage占用的内存很少。我怀疑你会发现这个优化有什么不同。 – VGR

回答

0

如果内存使用是您主要关注的问题,我会使用BufferedImage s和IndexColorModelTYPE_BYTE_BINARY)。这将完全反映您的byte[] imageColorPalette,并浪费很少的内存。他们也将相当快地画画。

这种方法将使用由初始使用的TYPE_INT_RGBBufferedImage小号所使用的存储器的约1/8,因为我们保留每像素4个比特,而不是每像素32位(一个int是32位)(加上一些当然,调色板的开销)。

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: [email protected]: 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); 
     } 
    }); 
} 

上面的代码将创建完全不透明(Transparency.OPAQUE)图像,即将占据整个16×16像素块。

如果您想要透明度(即所有像素都完全不透明或富勒透明),只需将IndexColorModel中的最后一个参数更改为要完全透明的调色板索引。

int transparentIndex = ...; 
IndexColorModel cm = new IndexColorModel(4, 16, palette, 0, false, transparentIndex); 
// ...everything else as above 

这将允许你的精灵有任何你想要的形状。

如果你想要半透明像素(Transparency.TRANSLUCENT),其中像素可以是半透明的,你也可以有。然后,您必须将palette数组更改为16 * 4条目,并将alpha值的样本作为每个条目的第4个样本(四元组)。然后用设定为truehasAlpha)的最后一个参数调用IndexColorModel构造:

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 

这将允许精灵的透明和非透明的部分之间更平滑的梯度。但在调色板中只有16种颜色,您不会有许多可用于透明度的条目。

注意,有可能重复使用Raster S和IndexColorModel这儿,在所有的上述实施例中,保存用于使用与不同的调色板相同的图像数据甚至图像使用相同的调色板图像,或另一存储器。但有一点需要注意,那就是图像共享栅格将是彼此的“实时视图”,因此如果您对其中一个进行了任何更改,您将会改变它们。但是如果你的图像从未改变,你可以利用这个事实。


这就是说,上述确实是保存内存和具有“合理”性能之间的妥协。如果性能(即每秒帧数)更重要,则忽略内存使用情况,并创建与您的图形卡/操作系统本机像素布局兼容的BufferedImage。您可以使用component.createCompatibleImage(...)(其中componentJComponent的子类)或gfxConfig.createCompatibleImage(...)(其中gfxConfig是从本地GraphicsEnvironment获得的GraphicsConfiguration)来完成此操作。

+0

有没有办法做到这一点,但允许透明像素? – user2649681

+0

PS:最初在IndexColorModel构造函数中存在一个错误。最后一个参数应该是'-1',因为没有透明度。 – haraldK

+0

查看透明度更新。 – haraldK