sun.awt.image.ByteInterleavedRaster.setPixels

sun.awt.image.ByteInterleavedRaster.setPixels

现在我正在学习Image。我想复制一张图片。我试试:

private BufferedImage mImage, mNewImage;
private int mWidth, mHeight;
private int[] mPixelData;

public void generate() {
    try {
        mImage = ImageIO.read(new File("D:\Documents\Pictures\image.png"));
        mWidth = mImage.getWidth();
        mHeight = mImage.getHeight();
        mPixelData = new int[mWidth * mHeight];
        // get pixel data from image
        for (int i = 0; i < mHeight; i++) {
            for (int j = 0; j < mWidth; j++) {
                int rgb = mImage.getRGB(j, i);
                int a = rgb >>> 24;
                int r = (rgb >> 16) & 0xff;
                int g = (rgb >> 8) & 0xff;
                int b = rgb & 0xff;
                int newRgb = (a << 24 | r << 16 | g << 8 | b);
                mPixelData[i * mWidth + j] = newRgb;
            }

            mNewImage = new BufferedImage(mWidth, mHeight, mImage.getType());
            WritableRaster raster = (WritableRaster) mNewImage.getData();
            raster.setPixels(0, 0, mWidth, mHeight, mPixelData);
            File file = new File("D:\Documents\Pictures\image2.png");
            ImageIO.write(mNewImage, "png", file);
        }
        } catch (IOException e) {
        e.printStackTrace();
        }
 }

但是我遇到了一个例外:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 222748 at sun.awt.image.ByteInterleavedRaster.setPixels(ByteInterleavedRaster.java:1108)

你的代码逻辑是健全的,但是上面的代码有很多小问题,所以我会试着一一指出::-)

  1. 您的 mPixelData 是打包的 ARGB 布局,这与 BufferedImage.TYPE_INT_ARGB 的使用相同。所以你要使用这个类型,而不是原始图像的类型。如果您从堆栈跟踪中看到,您的栅格类型是 ByteInterleavedRaster,并且这与您的 int[] 像素不兼容(使用原始类型可能引起的另一个问题是,它可能是TYPE_CUSTOM,无法使用此构造函数创建)。所以,首先改变:

    mNewImage = new BufferedImage(mWidth, mHeight, BufferedImage.TYPE_INT_ARGB);
    

    (注意:此更改后您仍会得到 IndexOutOfBoundsException,稍后我会 return)。

  2. BufferedImage.getData() 将为您提供 像素数据的副本 ,而不是对当前数据的引用。因此,在此副本上设置像素不会影响以后写入磁盘的数据。相反,使用 getRaster() 方法,它完全符合您的要求:

    WritableRaster raster = mNewImage.getRaster();
    
  3. Raster.setPixels(x, y, w, h, pixels) 方法需要一个数组每个数组元素包含一个样本(A、R、G 和 B 作为单独的样本)。这意味着您的数组长度仅为方法预期长度的四分之一,这最终是您看到的异常的原因。相反,因为你的数组是 int-packed ARGB 布局(这是你现在使用的类型的原生布局),你应该使用 setDataElements 方法:

    raster.setDataElements(0, 0, mWidth, mHeight, mPixelData);
    
  4. 最后,我想指出的是,循环中的所有位移都会简单地将所有像素解压缩为单个组件(A、R、G 和 B),然后将它们重新组合在一起再次......所以,在这种情况下,newRGB == rgb。但是您可能打算稍后在此处添加颜色操作,在这种情况下它是有意义的。 :-)


PS:如果您只想创建原始图像的精确副本,最快的方法可能是:

ColorModel cm = mImage.getColorModel();
WritableRaster raster = (WritableRaster) mImage.getData(); // Here we want a copy of the original image
mNewImage = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);