Manipulating the pixels within a BufferedImage through an Array - java

I'm currently following a series on Java game development from scratch. I understand most java and oop concepts but have very little experience when dealing with graphics and hardware acceleration.
The lines of code that I am questioning are:
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
The BufferedImage "image" variable is always the variable being drawn to the screen through a render method. Usually something like:
public void render() {
BufferStrategy bs = this.getBufferStrategy;
if(bs == null) { this.createBufferStrategy(3); return; }
Graphics g = bs.getDrawGraphics();
g.drawImage(image, 0, 0, WIDTH, HEIGHT, null);
g.dispose();
bs.show();
}
I understand the array of pixels contains every pixel within the BufferedImage, however, it seems as though everytime that array is filled with values it directly effects the contents of the "image" variable. There is never a method used to copy the pixels array values into the image.
Are these varibales actually linked in such a way? Does the use of:
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
create an automated link between the image and the array being created in the code above? Maybe I am going cray and just missed something but I have reviewed the code several times and not once is the "image" varibale manipulated after it's initial creation. (besides being rendered to the screen of course.) It's always the array "pixels" just being filled with different values that causes the change in the rendered image.
Some insight on this would be wonderful. Thank you in advance!

Why don't you call
image.getData()
instead of
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
image.getData() returns a copy of the Raster. getRaster() returns a WriteableRaster with the ability to modify pixels. I'm guessing but getRaster() probably returns a child of the image Raster and therefore is writeable if you modify the array. Try image.getData() to see if it works. If not, post back here and I'll take a closer look.
I looked into this further. The source code that comes with the JDK shows that image.getRaster().getDataBuffer().getData() returns the source data array. image.getData() indeed returns a copy. If the image is modified, the data in getData() will not be modified.
You can call getPixels on the returned Raster:
public int[] getPixels(int x,
int y,
int w,
int h,
int[] iArray)
Returns an int array containing all samples for a rectangle of pixels,
one sample per array element. An ArrayIndexOutOfBoundsException may be
thrown if the coordinates are not in bounds. However, explicit bounds
checking is not guaranteed.

Use int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); instead of image.getData(). image.getData() just returns a copy of the image data where image.getRaster() returns the original pixel array allowing to actually write pixels to it.

Related

How to move everything in Graphics2D by x,y coordinates.

I need to move already made BufferedImage by x,y coordinates and then draw another things on it with Graphics2D object. I tried to use this code to do that :
Graphics2D g = img.createGraphics();
g.translate(x, y);
but it doesn't work. Is there any way to move everything in Graphics2D object and then draw on it or I have to use this code to do that:
BufferedImage temp = new BufferedImage(img.getWidth(),img.getHeight(),BufferedImage.TYPE_INT_ARGB);
Graphics2D g = temp.createGraphics();
g.drawImage(img,x, y,null);
Using this code and then drawing only few elements rather than making whole image from scratch isn't big leap in performance so I think making new BufferedImage then drawing image on it isn't best way. I would rather just create Graphics2D object from already made image and then just move it by a few pixels diagonally, but I couldn't find the way to do that.
From the Graphics2d docs when you use translate:
All coordinates used in subsequent rendering operations on this graphics context are relative to this new origin.
You are defining a transformation that affects future operations. After calling translate if you were to call a method on graphics like draw3DRect(0, 0, ... snipped ... ) the starting coordinates 0,0 would be translated by x,y.
I think your best bet might be to use the methods of BufferedImage to move all the pixels before you get the graphics object. You have getRgb and setRgb
A naive example of moving the pixels:
BufferedImage buffImg = ImageIO.read(img);
int width = buffImg.getWidth();
int horizontalOffset = 10;
int verticalOffset = 10;
int widthToMove = width - horizontalOffset;
int heightToMove = buffImg.getHeight() - verticalOffset;
int[] rgb = buffImg.getRGB(0, 0, widthToMove, heightToMove, null, 0, widthToMove);
buffImg.setRGB(horizontalOffset,verticalOffset,widthToMove, heightToMove,rgb, 0, widthToMove);
This still leaves you with some work to do because there is a strip at the top and to the left that you need to fill with background colour.
If it's going to be used on big images you might want to use a buffer int[] and pass it to getRGB in a loop, getting and setting in chunks.

Layer multiple BufferedImages on top of one another?

I have multiple transparent BufferedImage instances which I'd like to layer on top of each other (aka Photoshop layers) and bake into one BufferedImage output. How do I do this?
I would say the best bet would be to take the buffered images, and create an additional one in order to have an object to append to. Then simply use the Graphics.drawImage() to place them on top of each other.
So something along these lines:
BufferedImage a = ImageIO.read(new File(filePath, "a.png"));
BufferedImage b = ImageIO.read(new File(filePath, "b.png"));
BufferedImage c = new BufferedImage(a.getWidth(), a.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics g = c.getGraphics();
g.drawImage(a, 0, 0, null);
g.drawImage(b, 0, 0, null);
Let's pretend that the first BufferedImage is named bi1 and the second bi2, while the image you want to layer them onto is target.
BufferedImage target=new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
Graphics2D targetGraphics=target.createImage();
targetGraphics.drawImage(bi1,0,0,null);//draws the first image onto it
int[] pixels2=((DataBufferInt) bi2.getRaster().getDataBuffer()).getData();
int[] pixelsTgt=((DataBufferInt) target.getRaster().getDataBuffer()).getData();
for(int a=0;a<pixels2.length;a++)
{
pixelsTgt[a]+=pixels2[a];//this adds the pixels together
}
Make sure that all three BufferedImage objects are of TYPE_INT_ARGB so that alpha is turned on. This may not give you the exact results that you had wanted if the two added together are more than the max integer, so you might want to add in something to help fix that. Pixels use bit shifts to add to colors with the integer ordered as AARRGGBB.
Also consider the AlphaComposite modes available to the graphics context, discussed here.

How do I create a BufferedImage from array containing pixels?

I get the pixels from BufferedImage using the method getRGB(). The pixels are stored in array called data[]. After some manipulation on data array, I need to create a BufferedImage again so that I can pass it to a module which will display the modified image, from this data array, but I am stuck with it.
I get the pixels from the BufferedImage using the method getRGB(). The
pixels are stored in array called data[].
Note that this can possibly be terribly slow. If your BufferedImage supports it, you may want to instead access the underlying int[] and directly copy/read the pixels from there.
For example, to fastly copy your data[] into the underlying int[] of a new BufferedImage:
BufferedImage bi = new BufferedImage( w, h, BufferedImage.TYPE_INT_ARGB );
final int[] a = ( (DataBufferInt) res.getRaster().getDataBuffer() ).getData();
System.arraycopy(data, 0, a, 0, data.length);
Of course you want to make sure that your data[] contains pixels in the same representation as your BufferedImage (ARGB in this example).
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Then set the pixels again.
bufferedImage.setRGB(x, y, your_value);
PS: as stated in the comments, please use the answer from #TacticalCoder
You can set the RGB (int) values for the pixels in the new image using the setRGB methods.

How to clone Image?

I have an Image. I need to make a exactly copy of it and save it to BufferedImage, but there is no Image.clone(). The thing should be inside a calculating loop and so it should be really fast, no pixel-by-pixel copying. What's the best in perfomance method to do this?
You can draw to a buffered image, so make a blank bufferedImage, create a graphics context from it, and draw your original image to it.
BufferedImage copyOfImage =
new BufferedImage(widthOfImage, heightOfImage, BufferedImage.TYPE_INT_RGB);
Graphics g = copyOfImage.createGraphics();
g.drawImage(originalImage, 0, 0, null);
There is another way:
BufferedImage copyOfImage = image.getSubimage(0, 0, image.getWidth, image.getHeight);
Image clone = original.getScaledInstance(original.getWidth(), -1, Image.SCALE_DEFAULT);
This might not be very pretty, but getScaledInstance returns, as the name suggests, an instance of your original Image object. Usually only used for resizing. -1 tells the method to keep the aspect ratio as it is
You can create a method that returns the subimage of the image you want to clone.
Such as:
public static BufferedImage clone(BufferedImage img)
{
return img.getSubimage(img.getMinX(), img.getMinY(), img.getWidth(), img.getHeight());
}

How to create a Raster from a pixel float array in Java?

I'm trying to take a BufferedImage, apply a Fourier transform (using jtransforms), and write the data back to the BufferedImage. But I'm stuck creating a new Raster to set the results back, am I missing something here?
BufferedImage bitmap;
float [] bitfloat = null;
bitmap = ImageIO.read(new File("filename"));
FloatDCT_2D dct = new FloatDCT_2D(bitmap.getWidth(),bitmap.getHeight());
bitfloat = bitmap.getData().getPixels(0, 0, bitmap.getWidth(), bitmap.getHeight(), bitfloat);
dct.forward(bitfloat, false);
But I'm stumped trying to finish off this line, what should I give the createRaster function? The javadocs for createRaster make little sense to me:
bitmap.setData(Raster.createRaster(`arg1`, `arg2`, `arg3`));
I'm starting to wonder if a float array is even necessary, but there aren't many examples of jtransforms out there.
Don't create a new Raster. Use WritableRaster.setPixels(int,int,int,int,float[]) to write the array back to the image.
final int w = bitmap.getWidth();
final int h = bitmap.getHeight();
final WritableRaster wr = bitmap.getData();
bitfloat = wr.getPixels(0, 0, w, h, bitfloat);
// do processing here
wr.setPixels(0, 0, w, h, bitfloat);
Note also that if you're planning to display this image, you should really copy it to a screen-compatible type; ImageIO seldom returns those.
I'm doing Google searches for FloatDCT_2D to see what package/library it's in, and it looks like there are several references to various sources, such as "edu.emory.mathcs.jtransforms.dct.FloatDCT_2D". Without knowing what custom library you're using, it's really hard to give you any advice on how to perform the transform.
My guess is in general, that you should read the input data from the original raster, perform the transform on the original data, then write the output to a new raster.
However, your last statement all on it's own looks odd... Raster.createRaster() looks like you're calling a static method with no parameters on a class you've never referenced in the code you've posted. How is that generating data for your bitmap??? Even in pseudo code, you would need to take the results of your transform and build the resultant raster.

Categories