In some Java code running on Windows, I'm reading some large blocks of RGB data from disk and want to display this to screen as quickly as possible. The RGB data is 8 bits per channel without any alpha. Currently I have code like the following to create the BufferedImage.
BufferedImage getBufferedImage(File file, int width, int height) {
byte[] rgbData = readRGBFromFile(file);
WritableRaster raster = Raster.createInterleavedRaster(
rgbData, width, height,
width * 3, // scanlineStride
3, // pixelStride
new int[]{0, 1, 2}, // bandOffsets
null);
ColorModel colorModel = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[]{8, 8, 8}, // bits
false, // hasAlpha
false, // isPreMultiplied
ComponentColorModel.OPAQUE,
DataBuffer.TYPE_BYTE);
return new BufferedImage(colorModel, raster, false, null);
}
The problem is that the performance of rendering this to the screen is pretty slow. Around 250 - 300 ms. I've read that for the best performance you need to display in a BufferedImage that's compatible with the screen. To do that, I pass the buffered image returned from the above method to a method like this.
BufferedImage createCompatibleImage(BufferedImage image)
{
GraphicsConfiguration gc = GraphicsEnvironment.
getLocalGraphicsEnvironment().
getDefaultScreenDevice().
getDefaultConfiguration();
BufferedImage newImage = gc.createCompatibleImage(
image.getWidth(),
image.getHeight(),
Transparency.TRANSLUCENT);
Graphics2D g = newImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}
That method essentially converts it from RGB to ARGB on Windows and it really speeds up the displaying, but this method takes ~300 ms for a 1600 x 1200 RGB data block. So now I've basically traded the performance hit of the drawing problem to a converting problem.
300ms is about the same time as it takes to load the RGB data from disk. I would think I could do something faster.
Is there a better way I can do the conversion? Or would it help if I modified the RGB data and added an alpha channel myself beforehand? If so what would my Raster and ColorModel look like. Also, since my RGB data doesn't contain transparency can I get any performance improvements by using pre multiplied alpha or something?
Sorry, bit I'm a little lost on this ColorModel, Raster stuff.
Thanks!
I realize this is a really old question, I'm just posting this for anybody else who might stumble upon this question looking for more options. I had an issue recently where I was attempting to take a large (720p) RGB byte[] and render it to a BufferedImage
. The original implementation I was using looked something like this (simplified here):
public void processFrame(byte[] frame, int width, int height)
{
DataBuffer videoBuffer = new DataBufferByte(frame,frame.length);
BufferedImage currentImage = new BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR);
ComponentSampleModel sampleModel = new ComponentSampleModel(DataBuffer.TYPE_BYTE,width,height,3,width*3,new int[] {2,1,0});
Raster raster = Raster.createRaster(sampleModel,videoBuffer,null);
currentImage.setData(raster);
}
Even with optimizations like creating the BufferedImage
and ComponentSampleModel
once and reusing them, the final step of calling setData
on the BufferedImage
was still taking on the order of 50-60 milliseconds, which is unacceptable.
What I ended up realizing is that, at least for my scenario, you can actually write to the backing byte array of the BufferedImage
directly and bypass most of the intermediate processing (assuming the backing metadata for the image is already correct). So I changed my code to look like this:
public void processFrame(byte[] frame, int width, int height)
{
BufferedImage currentImage = new BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR);
byte[] imgData = ((DataBufferByte)currentImage.getRaster().getDataBuffer()).getData();
System.arraycopy(frame,0,imgData,0,frame.length);
}
Just by doing this, my performance improved by about a factor of 20. I now process the same frames in 3-5 milliseconds instead of 50-60 milliseconds.
This may not be applicable for all cases, but I thought I'd share in case someone else finds it useful.