Layer multiple BufferedImages on top of one another? - java

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.

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.

High performance red-filter on Java BufferedImage

Let's say I have a BufferedImage with ARGB channels. I can turn this image into a grayscale image simply by doing
BufferedImage copy = new BufferedImage(image.getWidth(), image.getHeight(),
BufferedImage.TYPE_BYTE_GRAY);
Graphics g = copy.getGraphics().create();
g.drawImage(image, 0, 0, null);
g.dispose();
There are a couple of other methods to do grayscale conversion I'm aware of, but this one works good for my program. I can also (and do) enhance the contrast of the image by doing this:
RescaleOp op;
op = new RescaleOp(1.0f, darken, null);
op.filter(copy, copy);
op = new RescaleOp(brighten, 0.0f, null);
op.filter(copy, copy);
But there's a problem. Sometimes there are slightly dark-red parts of my image that I need to isolate, which are close to slightly bright regions, that is, regions with a high red value (such as yellow and purple). I need to isolate these red regions. How can I do this efficiently?
Manually, I would like something like
for each pixel p in original
new.p = grayscale(Math.max(Math.abs(p.red - p.green), Math.abs(p.red - p.blue)))
Can I do this more efficiently using built-in filters or the like? I'm not looking for an exact filter - just something to help me on the way of isolating these red areas a bit. This kind of code makes me think there's an efficient way: this is for producing lower-quality grayscale images, but it is very fast.
ImageFilter filter = new GrayFilter(true, 50);
ImageProducer producer = new FilteredImageSource(colorImage.getSource(), filter);
Image mage = this.createImage(producer);
Thanks for any help and suggestions!

Performing setRGB on BufferedImage changes pixel to black instead of color

** Important update, see below! **
I am creating a program that changes the pixels of a BufferedImage to a certain color when that pixel fulfills a set of conditions in Java. However, when I write the image to disk, the pixels that should be colored are instead black.
First I define the color, using RGB codes:
Color purple = new Color(82, 0, 99);
int PURPLE = purple.getRGB();
Then I read the image I want to alter from a File into a BufferedImage called "blank":
BufferedImage blank = ImageIO.read(new File("some path"));
Now, loop through the pixels, and when a pixel at location (x, y) matches a criteria, change its color to purple:
blank.setRGB(x, y, PURPLE);
Now, write "blank" to the disk.
File output = new File("some other path");
ImageIO.write(blankIn, "png", output); // try-catch blocks intentionally left out
The resulting file should be "blank" with some purple pixels, but the pixels in question are instead black. I know for a fact that the issue is with setRGB and NOT any import or export functions, because "blank" itself is a color image, and gets written to file as such. I read around and saw a lot of posts recommending that I use Graphics2D and to avoid setRGB, but with no discussion of pixel-by-pixel color changing.
I also tried direct bit manipulation, like this:
blank.setRGB(x, y, ((82 << 16) + (0 << 8) + 99));
I'm probably doing that wrong, but if I put it in correctly it wouldn't matter, because the pixels are getting set to transparent when I do this (regardless of what the numbers say, which is very strange, to say the least).
** When I try this:
blank.setRGB(x, y, Color.RED.getRGB());
My output file is grayscale, so that means setRGB is, in fact, modifying my picture in grayscale. I think this is actually a rather simple issue, but the solution eludes me.
Based on the insights in https://stackoverflow.com/a/21981173 that you found yourself ... (a few minutes after posting the question) ... it seems that it should be sufficient to simply convert the image into ARGB directly after it was loaded:
public static BufferedImage convertToARGB(BufferedImage image)
{
BufferedImage newImage = new BufferedImage(
image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}
The original image that was imported into Java was actually grayscale, so when Java read it into the BufferedImage, it simply imported it as a grayscale BufferedImage. By adding a very small but imperceptible colored dot in the corner of my image, I was able to get Java to output a correctly colored image.
Unfortunately, this is only a half solution, because I do not know how to fix this programmatically.
SOLUTION:
Convert the BufferedImage from grayscale to ARGB with this snippet:
BufferedImage blank2 = blank;
// Create temporary copy of blank
blank = new BufferedImage(blank.getWidth(), blank.getHeight(), BufferedImage.TYPE_INT_ARGB);
// Recreate blank as an ARGB BufferedImage
ColorConvertOp convert = new ColorConvertOp(null);
// Now create a ColorConvertOp object
convert.filter(blank2, blank);
// Convert blank2 to the blank color scheme and hold it in blank
You want to add this right after blank = ImageIO.read(new File("some path")).

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

Categories