0

Now I am learning about Image.I want to copy an image. I try:

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();
        }
 }

But I got an exception:

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

CoXier
  • 162
  • 1
  • 10

1 Answers1

1

The logic in your code is sane, but there are multiple minor issues with the code above, so I'll try to point them out one by one: :-)

  1. Your mPixelData is in packed ARGB layout, this is the same as use by BufferedImage.TYPE_INT_ARGB. So you want to use this type, rather than the type of the original image. If you see from your stack trace, the type of your raster is ByteInterleavedRaster, and this is not compatible with your int[] pixels (another issue that may arise from using the original type, is that it may be TYPE_CUSTOM, which can't be created using this constructor). So, first change:

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

    (Note: You will still get an IndexOutOfBoundsException after this change, I'll return to that later).

  2. BufferedImage.getData() will give you a copy of the pixel data, rather than a reference to the current data. So, setting the pixels on this copy will have no effect on the data being written to disk later. Instead, use the getRaster() method, that does exactly what you want:

    WritableRaster raster = mNewImage.getRaster();
    
  3. The Raster.setPixels(x, y, w, h, pixels) method expects an array containing one sample per array element (A, R, G and B as separate samples). This means that the length of your array is only one fourth of what the method expects, and this is finally the cause of the exception you see. Instead, as you array is in int-packed ARGB layout (which is the native layout of the type you now use), you should use the setDataElements method:

    raster.setDataElements(0, 0, mWidth, mHeight, mPixelData);
    
  4. Finally, I just like to point out that all the bit shifting in your loop will simply unpack all the pixels into single components (A, R, G and B) and then pack them back together again... So, newRGB == rgb in this case. But you are maybe planning to add color manipulation here later, in which case it makes sense. :-)


PS: If all you want to do is creating an exact copy of the original image, the fastest way to do it is probably:

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);
Harald K
  • 26,314
  • 7
  • 65
  • 111