现在我正在学习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();
}
}
但我有一个例外:
线程“主”中的异常java.lang.ArrayIndexOutOfBoundsException:在sun.awt.image.ByteInterleavedRaster.setPixels(ByteInterleavedRaster.java:1108)上的222748
最佳答案
您代码中的逻辑是理智的,但是上面的代码有多个小问题,因此,我将一一指出::-)
您的mPixelData
采用压缩的ARGB布局,与BufferedImage.TYPE_INT_ARGB
所使用的相同。因此,您要使用此类型,而不是原始图像的类型。如果从堆栈跟踪中看到,则栅格类型为ByteInterleavedRaster
,并且与int[]
像素不兼容(使用原始类型可能会引起的另一个问题是,它可能是TYPE_CUSTOM
,无法使用此构造函数创建)。因此,首先更改:
mNewImage = new BufferedImage(mWidth, mHeight, BufferedImage.TYPE_INT_ARGB);
(注意:此更改后,您仍然会得到
IndexOutOfBoundsException
,我稍后会再讲)。BufferedImage.getData()
将为您提供像素数据的副本,而不是当前数据的引用。因此,在此副本上设置像素不会影响以后写入磁盘的数据。而是使用getRaster()
方法,该方法正是您想要的:WritableRaster raster = mNewImage.getRaster();
Raster.setPixels(x, y, w, h, pixels)
方法期望一个数组每个数组元素包含一个样本(A,R,G和B作为单独的样本)。这意味着您的数组长度仅为该方法预期值的四分之一,这最终是您看到异常的原因。相反,由于数组采用的是int
打包的ARGB布局(这是您现在使用的类型的本机布局),因此应使用setDataElements
方法:raster.setDataElements(0, 0, mWidth, mHeight, mPixelData);
最后,我想指出的是,循环中的所有位移都会简单地将所有像素解压缩为单个分量(A,R,G和B),然后再次将它们重新打包在一起...因此,这个案例。但是您可能打算稍后再在此处添加颜色处理,在这种情况下,这很有意义。 :-)
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);
关于java - sun.awt.image.ByteInterleavedRaster.setPixels,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39318665/