本文介绍了在BufferedImage中快速加载和绘制RGB数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一些运行在Windows上的Java代码中,我正在从磁盘读取一些大的RGB数据块,并希望尽快将其显示在屏幕上。 RGB数据每个通道8位,不含任何字母。目前我的代码如下创建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);

返回新的BufferedImage(colorModel,raster,false,null);
}

问题在于将其渲染到屏幕的性能非常慢。大约250 - 300毫秒。我已经阅读过,为了获得最佳性能,您需要在与屏幕兼容的BufferedImage中显示。为此,我将从上述方法返回的缓冲图像传递给像这样的方法。

  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;



$ b $ p
$ b

该方法在Windows上基本上将其从RGB转换为ARGB,并且它实际上可以加速显示,但这种方法对于1600 x 1200 RGB数据块需要〜300 ms。所以现在我基本上把绘图问题的性能问题转化为转换问题。

与从磁盘加载RGB数据所用的时间大致相同。我想我可以做得更快。



有更好的方法可以做转换吗?或者,如果我预先修改了RGB数据并添加了一个Alpha通道,它会有帮助吗?如果是的话,我的Raster和ColorModel会是什么样子。此外,由于我的RGB数据不包含透明度,我可以通过使用预乘alpha或其他东西来获得任何性能改进吗?



对不起,我有点失落这个ColorModel,光栅的东西。



谢谢!

解决方案

与此我有一个体面的答案适用于Windows,如果当前的图形配置使用ARGB整数打包栅格。

我所做的是先创建兼容的BufferedImage,然后手动将我的RGB字节数组转换为ARGB int数组。然后,我从兼容的BufferedImage中获取Raster,并将其ARGB整数写入其中。这要快得多。

我也有一个类来检查兼容的BufferedImage是否符合我期望的格式,如果不是,则默认为较慢的方法。



这是班级。希望它可以帮助你。

  / ** 
*该类可以读取文件中的RGB图像数据块并返回BufferedImage。
*它可能使用优化技术加载依赖于Windows上
*默认图像格式假设的图像。
* /
public class RGBImageLoader
{
private byte [] tempBuffer_;
private boolean fastLoading_;

public RGBImageLoader()
{
fastLoading_ = canUseFastLoadingTechnique();
}

private boolean canUseFastLoadingTechnique()
{
//创建一个与屏幕兼容的图像
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment()。 。getDefaultScreenDevice()getDefaultConfiguration();
BufferedImage image = gc.createCompatibleImage(100,100,Transparency.TRANSLUCENT);

//在Windows上,这应该是一个ARGB整数打包栅格。如果是,那么我们可以
//使用我们的优化技术

if(image.getType()!= BufferedImage.TYPE_INT_ARGB)
return false;

WritableRaster raster = image.getRaster();

if(!(raster instanceof IntegerInterleavedRaster))
return false; $!
$ b if(!(raster.getDataBuffer()instanceof DataBufferInt))
return false; $!
$ b if(!(image.getColorModel()instanceof DirectColorModel))
return false;

DirectColorModel colorModel =(DirectColorModel)image.getColorModel(); $!
$ b if(!(colorModel.getColorSpace()instanceof ICC_ColorSpace)||
colorModel.getNumComponents()!= 4 ||
colorModel.getAlphaMask()!= 0xff000000 ||
colorModel.getRedMask()!= 0xff0000 ||
colorModel.getGreenMask()!= 0xff00 ||
colorModel.getBlueMask()!= 0xff)
return false; $()
$ b if(raster.getNumBands()!= 4 ||
raster.getNumDataElements()!= 1 ||
!(raster.getSampleModel()instanceof SinglePixelPackedSampleModel))
返回false;

返回true;

$ b $ public BufferedImage loadImage(File file,int width,int height,long imageOffset)throws IOException
{
if(fastLoading_)
return loadImageUsingFastTechnique (文件,宽度,高度,imageOffset);
else
return loadImageUsingCompatibleTechnique(file,width,height,imageOffset);

$ b $ private BufferedImage loadImageUsingFastTechnique(File file,int width,int height,long imageOffset)throws IOException
{
int sizeBytes = width * height * 3;

//确保缓冲区足够大
if(tempBuffer_ == null || tempBuffer_.length< sizeBytes)
tempBuffer_ = new byte [sizeBytes];

RandomAccessFile raf = null;
尝试
{
raf = new RandomAccessFile(file,r);

raf.seek(imageOffset);

int bytesRead = raf.read(tempBuffer_,0,sizeBytes);
if(bytesRead!= sizeBytes)
throw new IOException(Invalid byte count。should be+ sizeBytes +not+ bytesRead);

GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment()。getDefaultScreenDevice()。getDefaultConfiguration();
BufferedImage image = gc.createCompatibleImage(width,height,Transparency.TRANSLUCENT);
WritableRaster raster = image.getRaster();
DataBufferInt dataBuffer =(DataBufferInt)raster.getDataBuffer();

addAlphaChannel(tempBuffer_,sizeBytes,dataBuffer.getData());

return image;
}
finally
{
try
{
if(raf!= null)
raf.close();
}
catch(Exception ex)
{
}
}
}

private BufferedImage loadImageUsingCompatibleTechnique(File file,int width,int height,long imageOffset)throws IOException
{
int sizeBytes = width * height * 3;

RandomAccessFile raf = null;
尝试
{
raf = new RandomAccessFile(file,r);

//让我们导航到偏移
raf.seek(imageOffset);

DataBufferByte dataBuffer = new DataBufferByte(sizeBytes);
byte [] bytes = dataBuffer.getData();

int bytesRead = raf.read(bytes,0,sizeBytes);
if(bytesRead!= sizeBytes)
throw new IOException(Invalid byte count。should be+ sizeBytes +not+ bytesRead);

WritableRaster raster = Raster.createInterleavedRaster(dataBuffer,// dataBuffer
width,// width
height,// height
width * 3,// scanlineStride
3,// pixelStride
new int [] {0,1,2},// bandOffsets
null); //位置

ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),// ColorSpace
new int [] {8,8,8},// bits
false,// hasAlpha
false,// isPreMultiplied
ComponentColorModel.OPAQUE,DataBuffer.TYPE_BYTE);

BufferedImage loadImage = new BufferedImage(colorModel,raster,false,null);

//将其转换为与当前屏幕兼容的缓冲图像。
//不理想的创建这个图像两次....
BufferedImage image = createCompatibleImage(loadImage);

return image;
}
finally
{
try
{
if(raf!= null)
raf.close();
}
catch(Exception ex)
{
}
}
}

private 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;


$ b $ private void addAlphaChannel(byte [] rgbBytes,int bytesLen,int [] argbInts)
{
for(int i = 0, j = 0; i {
argbInts [j] =((byte)0xff)< 24 | // Alpha
(rgbBytes [i] (rgbBytes [i + 1]<< 8)& (0xff00)| // Green
(rgbBytes [i + 2])& (0xff的); //蓝色
}
}

}


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!

解决方案

After playing around with this I have a decent answer that works for Windows if the current graphics configuration is using ARGB integer packed rasters.

What I do is create the compatible BufferedImage first, then I manually convert my RGB bytes array to an ARGB int array. Then I get the Raster from the compatible BufferedImage and write my ARGB ints into it. This is much faster.

I also have a class that checks if the compatible BufferedImage is in the format I expect, if it isn't it defaults to the older slower approach.

Here is the class. Hope it helps you.

/**
 * This class can read chunks of RGB image data out of a file and return a BufferedImage.
 * It may use an optimized technique for loading images that relies on assumptions about the
 * default image format on Windows.
 */
public class RGBImageLoader
{
    private byte[] tempBuffer_;
    private boolean fastLoading_;

    public RGBImageLoader()
    {
        fastLoading_ = canUseFastLoadingTechnique();
    }

    private boolean canUseFastLoadingTechnique()
    {
        // Create an image that's compatible with the screen
        GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        BufferedImage image = gc.createCompatibleImage(100, 100, Transparency.TRANSLUCENT);

        // On windows this should be an ARGB integer packed raster. If it is then we can
        // use our optimization technique

        if(image.getType() != BufferedImage.TYPE_INT_ARGB)
            return false;

        WritableRaster raster = image.getRaster();

        if(!(raster instanceof IntegerInterleavedRaster))
            return false;

        if(!(raster.getDataBuffer() instanceof DataBufferInt))
            return false;

        if(!(image.getColorModel() instanceof DirectColorModel))
            return false;

        DirectColorModel colorModel = (DirectColorModel) image.getColorModel();

        if(!(colorModel.getColorSpace() instanceof ICC_ColorSpace) ||
             colorModel.getNumComponents() != 4 ||
             colorModel.getAlphaMask() != 0xff000000 ||
             colorModel.getRedMask() != 0xff0000 ||
             colorModel.getGreenMask() != 0xff00 ||
             colorModel.getBlueMask() != 0xff)
            return false;

        if(raster.getNumBands() != 4 ||
           raster.getNumDataElements() != 1 ||
           !(raster.getSampleModel() instanceof SinglePixelPackedSampleModel))
            return false;

        return true;
    }

    public BufferedImage loadImage(File file, int width, int height, long imageOffset) throws IOException
    {
        if(fastLoading_)
            return loadImageUsingFastTechnique(file, width, height, imageOffset);
        else
            return loadImageUsingCompatibleTechnique(file, width, height, imageOffset);
    }

    private BufferedImage loadImageUsingFastTechnique(File file, int width, int height, long imageOffset) throws IOException
    {
        int sizeBytes = width * height * 3;

        // Make sure buffer is big enough
        if(tempBuffer_ == null || tempBuffer_.length < sizeBytes)
            tempBuffer_ = new byte[sizeBytes];

        RandomAccessFile raf = null;
        try
        {
            raf = new RandomAccessFile(file, "r");

            raf.seek(imageOffset);

            int bytesRead = raf.read(tempBuffer_, 0, sizeBytes);
            if (bytesRead != sizeBytes)
                throw new IOException("Invalid byte count. Should be " + sizeBytes + " not " + bytesRead);

            GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
            BufferedImage image = gc.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
            WritableRaster raster = image.getRaster();
            DataBufferInt dataBuffer = (DataBufferInt) raster.getDataBuffer();

            addAlphaChannel(tempBuffer_, sizeBytes, dataBuffer.getData());

            return image;
        }
        finally
        {
            try
            {
                if(raf != null)
                raf.close();
            }
            catch(Exception ex)
            {
            }
        }
    }

    private BufferedImage loadImageUsingCompatibleTechnique(File file, int width, int height, long imageOffset) throws IOException
    {
        int sizeBytes = width * height * 3;

        RandomAccessFile raf = null;
        try
        {
            raf = new RandomAccessFile(file, "r");

            // Lets navigate to the offset
            raf.seek(imageOffset);

            DataBufferByte dataBuffer = new DataBufferByte(sizeBytes);
            byte[] bytes = dataBuffer.getData();

            int bytesRead = raf.read(bytes, 0, sizeBytes);
            if (bytesRead != sizeBytes)
                throw new IOException("Invalid byte count. Should be " + sizeBytes + " not " + bytesRead);

            WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, // dataBuffer
                            width, // width
                            height, // height
                            width * 3, // scanlineStride
                            3, // pixelStride
                            new int[]{0, 1, 2}, // bandOffsets
                            null); // location

            ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), // ColorSpace
                            new int[]{8, 8, 8}, // bits
                            false, // hasAlpha
                            false, // isPreMultiplied
                            ComponentColorModel.OPAQUE, DataBuffer.TYPE_BYTE);

            BufferedImage loadImage = new BufferedImage(colorModel, raster, false, null);

            // Convert it into a buffered image that's compatible with the current screen.
            // Not ideal creating this image twice....
            BufferedImage image = createCompatibleImage(loadImage);

            return image;
        }
        finally
        {
            try
            {
                if(raf != null)
                raf.close();
            }
            catch(Exception ex)
            {
            }
        }
    }

    private 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;
    }


    private void addAlphaChannel(byte[] rgbBytes, int bytesLen, int[] argbInts)
    {
        for(int i=0, j=0; i<bytesLen; i+=3, j++)
        {
            argbInts[j] = ((byte) 0xff) << 24 |                 // Alpha
                        (rgbBytes[i] << 16) & (0xff0000) |      // Red
                        (rgbBytes[i+1] << 8) & (0xff00) |       // Green
                        (rgbBytes[i+2]) & (0xff);               // Blue
        }
    }

}

这篇关于在BufferedImage中快速加载和绘制RGB数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-05 02:48