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());
}
Related
Original
https://drive.google.com/file/d/1B3xxfWkGsMs2_MQ_bUQ8_ALYI0DL-LIo/view?usp=sharing
When saved to file
https://drive.google.com/file/d/1z5euXupeHmiFebch4A39fVqGukoUiK0p/view?usp=sharing
When printed to canvas
https://drive.google.com/file/d/1VouD-ygf0pPXFFx9Knr4pv44FHMtoqcV/view?usp=sharing
BufferedImage temp = bImg.getSubimage(100, 100, (int)imgWidth - 100, (int)imgHeight - 100);
try{
ImageIO.write(temp, "png", new File("test.png"));
}catch(Exception e){
e.printStackTrace();
}
gc.drawImage(SwingFXUtils.toFXImage(temp, null), 100, 100);
For some reason if I print an image to the canvas, it is different than if I save the same image to a file. When I save it to a file it correctly calculates the subImage but when I print it to the canvas it disregards the x and y coords I give it and takes a subImage using (0,0) as (x,y) with the given width and height.
From the documentation of the getSubimage method:
Returns a subimage defined by a specified rectangular region. The returned BufferedImage shares the same data array as the original image.
The sub-image is just a “window” into the original image; they are using the same pixel data.
The SwingFXUtils.toFXImage documentation states:
Snapshots the specified BufferedImage and stores a copy of its pixels into a JavaFX Image object, creating a new object if needed.
While it would certainly make sense to only copy the pixels in the source image’s dimensions, the above words don’t make it completely clear that it won’t copy the entire pixel data buffer, thus ignoring the boundaries of a sub-image. I would consider this a bug, but I can see where there might be an argument that it’s not.
In the meantime, you can work around this by extracting a sub-image yourself:
BufferedImage cropped = new BufferedImage(
(int) imgWidth - 100,
(int) imgHeight - 100,
bImg.getType());
Graphics g = cropped.getGraphics();
g.drawImage(bImg, -100, -100, null);
g.dispose();
gc.drawImage(SwingFXUtils.toFXImage(cropped, null), 100, 100);
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.
** 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")).
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.
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.