Buffered image pixel manipulation
-
09-02-2021 - |
Question
I have this code:
public Image toNegative()
{
int imageWidth = originalImage.getWidth();
int imageHeight = originalImage.getHeight();
int [] rgb = null; // new int[imageWidth * imageWidth];
originalImage.getRGB(0, 0, imageWidth, imageHeight, rgb, 0,imageWidth);
for (int y = 0; y < imageHeight; y++)
{
for (int x = 0; x < imageWidth; x++)
{
int index = y * imageWidth + x;
int R = (rgb[index] >> 16) & 0xff; //bitwise shifting
int G = (rgb[index] >> 8) & 0xff;
int B = rgb[index] & 0xff;
R = 255 - R;
G = 255 - R;
B = 255 - R;
rgb[index] = 0xff000000 | (R << 16) | (G << 8) | B;
}
}
return getImageFromArray(rgb, imageWidth, imageHeight);
}
It throws NPE or when array is used or ArrayOutOfBoundsException when I allocate array before passing it getRGB. I check in debugger and image has the size and is allocated.
UPDATE: The getRGB
/**
* Returns an array of integer pixels in the default RGB color model
* (TYPE_INT_ARGB) and default sRGB color space,
* from a portion of the image data. Color conversion takes
* place if the default model does not match the image
* <code>ColorModel</code>. There are only 8-bits of precision for
* each color component in the returned data when
* using this method. With a specified coordinate (x, y) in the
* image, the ARGB pixel can be accessed in this way:
* </p>
*
* <pre>
* pixel = rgbArray[offset + (y-startY)*scansize + (x-startX)]; </pre>
*
* <p>
*
* An <code>ArrayOutOfBoundsException</code> may be thrown
* if the region is not in bounds.
* However, explicit bounds checking is not guaranteed.
*
* @param startX the starting X coordinate
* @param startY the starting Y coordinate
* @param w width of region
* @param h height of region
* @param rgbArray if not <code>null</code>, the rgb pixels are
* written here
* @param offset offset into the <code>rgbArray</code>
* @param scansize scanline stride for the <code>rgbArray</code>
* @return array of RGB pixels.
* @see #setRGB(int, int, int)
* @see #setRGB(int, int, int, int, int[], int, int)
*/
public int[] getRGB(int startX, int startY, int w, int h,
int[] rgbArray, int offset, int scansize) {
int yoff = offset;
int off;
Object data;
int nbands = raster.getNumBands();
int dataType = raster.getDataBuffer().getDataType();
switch (dataType) {
case DataBuffer.TYPE_BYTE:
data = new byte[nbands];
break;
case DataBuffer.TYPE_USHORT:
data = new short[nbands];
break;
case DataBuffer.TYPE_INT:
data = new int[nbands];
break;
case DataBuffer.TYPE_FLOAT:
data = new float[nbands];
break;
case DataBuffer.TYPE_DOUBLE:
data = new double[nbands];
break;
default:
throw new IllegalArgumentException("Unknown data buffer type: "+
dataType);
}
if (rgbArray == null) {
rgbArray = new int[offset+h*scansize];
}
for (int y = startY; y < startY+h; y++, yoff+=scansize) {
off = yoff;
for (int x = startX; x < startX+w; x++) {
rgbArray[off++] = colorModel.getRGB(raster.getDataElements(x,
y,
data));
}
}
return rgbArray;
}
La solution
Your code will throw a NullPointerException
because you are never assigning a non-null reference to the rgb
variable. Hence, references to it (e.g. rgb[index]
) will generate the exception. If you wish to pass in a null array to getRGB you need to ensure you assign the result array returned by the method; e.g.
int[] rgb = originalImage.getRGB(0, 0, imageWidth, imageHeight, rgb, 0,imageWidth);
If you were to uncomment the code commented out there is a bug in that you are allocating the array as imageWidth * imageWidth
instead of imageWidth * imageHeight
, which is why you're seeing an ArrayIndexOutOfBoundsException
.
Autres conseils
There are two problems:
The width of the array is not the width of the image but the "scan size" (some image sizes get padded with extra pixels)
If you call
getRGB()
with anull
array, the method will create an array but it won't change thergb
reference - Java doesn't support "out parameters".
To make this work, use
rgb = originalImage.getRGB(0, 0, imageWidth, imageHeight, null, 0,imageWidth);