I'm thinking about the best way to do two things in Java:
merge two images, the background one in .PNG and the other in .GIF or .PNG (has transparency and is to overlap the first one);
convert the merged image to .GIF (with transparency).
I don't want to render them, just to handle the images in the java class and write the resultant image to a file.
Can anyone help me? What's the best way to do this?
Thank you!
EDIT:
Thank you all for the suggestions!
This was what I ended up using! Pretty simple!
BufferedImage background = ImageIO.read(new File("image.jpg"));
WritableRaster raster = background.getRaster();
BufferedImage layer = ImageIO.read(new File("overlay.png"));
Graphics2D g2d = (Graphics2D)background.getGraphics();
g2d.drawImage(layer,72,80,null);
About the second problem, I still can't save this with .gif extension with transparency.
This
ImageIO.write(bufferedImage,"gif",file);
creates the .gif image file but it loses the transparency!
Does anyone know how can I do this? JAI also doesn't have the gif encoder.
Thank you.
Not sure of your application, but if this is server-based high performance stuff, I've had much better results shelling out to ImageMagick than using Java's image libraries.
Maybe this link could help ?
It is the same to render an image on a component and to paint an image.
Instead of painting in the paint method, you'll have to create a method createImage(). The sample provided in the link could be adapted like this:
(in java, painting on a component, on an image, on printer output... is everywhere the same. You get a graphics, you paint).
public BufferedImage createImage(Graphics g) {
BufferedImage image - new BufferedImage(...);
Graphics2D g2 = image.getGraphics();
Point2D center = new Point2D.Float(image.getHeight( ) / 2, image.getWidth( ) / 2);
AffineTransform at = AffineTransform.getTranslateInstance(center.getX( ) - (bi2.getWidth( ) / 2), center.getY( ) - (bi2.getHeight( ) / 2));
g2.transform(at);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawImage(bi2, 0, 0, null);
Composite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .35f);
g2.setComposite(c);
at = AffineTransform.getTranslateInstance(center.getX( ) - (bi1.getWidth( ) / 2), center.getY( ) - (bi1.getHeight( ) / 2));
g2.setTransform(at);
g2.drawImage(bi1, 0, 0, null);
return image;
}
If you don't mind adding an external dependency, you can use the JMagick bindings to ImageMagick.
Definintely check out JAI (api).
It will allow you to load and store images that were encoded in basically any known format (png and gif included) and allow you to efficiently operate on the images once they've been loaded.
In particular you're probably looking for something like the CompositeDescriptor.
Related
I've been working on optimizing color PNG images for storage size by using the TYPE_BYTE_INDEXED BufferedImage to limit the number of unique colors each image can contain. I noticed that when I save an existing image to this format, the white (255,255,255) background color becomes an off-white color (252,252,252).
Is there any way to prevent this color shift from happening? I found this question which has a solution but it feels very hacky and is much slower than the Graphics2D implementation of drawImage.
Here's my code currently that's producing this issue. The original image is being loaded as a TYPE_3BYTE_BGR BufferedImage.
BufferedImage image = ImageIO.read(imageOutputPath.toFile());
BufferedImage indexedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
Graphics2D g = indexedImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
I know that this type of BufferedImage supports white as I've tried adding white margins around the image and it displays correctly but with the off-white image centered on the page. It just seems like the white in my image isn't converting correctly.
Any ideas and help would be greatly appreciated. Thanks!
I'm developing a little graphic engine using Canvas in JavaFX. In some point I had to render an off screen image, and then print it on my primary canvas using its GraphicContext.
I'm using this code right now:
private Canvas offScreenCanvas;
private GraphicsContext offScreenGraphic;
private SnapshotParameters parameters;
private WritableImage offScreenImage;
[...]
offScreenCanvas = new Canvas(WIDTH, HEIGHT);
offScreenGraphic = offScreenCanvas.getGraphicsContext2D();
parameters = new SnapshotParameters();
parameters.setFill(Color.TRANSPARENT);
[...]
offScreenImage = offScreenCanvas.snapshot(parameters, offScreenImage);
graphic.setGlobalBlendMode(BlendMode.HARD_LIGHT);
graphic.drawImage(offScreenImage, 0, 0);
graphic.setGlobalBlendMode(BlendMode.SRC_OVER);
My problem is the method snaphot() takes too much time, ~14ms, in each execution. I need to update the canvas at least at 60fps, so this consumes practically all the time I have to draw.
Is there another way to get an Image or WritableImage from a canvas? Maybe another different process?
This is another method to obtain a visual equivalent result, without reduce performance.
I have used java.awt clases, instead of JavaFX clases. The creation of a java.awt.image.BufferedImage offers the possibility to get a java.awt.Graphics2D where you can draw using other methods. The main problem here is that draw big images consumes a lot of time using this libraries, but you can create a scaled image. In my case, I have created a quarter-size BufferedImage, and I have drawn all the objects using that scale factor.
At the end of the draw process, just convert the BufferedImage, to a javafx.scene.image.Image, using:
SwingFXUtils.WritableImage toFXImage(BufferedImage bimg, WritableImage wimg);
Then print it on the main canvas using:
graphic.drawImage(Image image, 0, 0, WIDTH, HEIGHT);
To fill all the canvas with the image.
Finally, the scale factor is configurable, so if you need a detailed image, just use a higher value. For me, a 25-percent-size image is enough because I am drawing gradients. Now, it takes 1-3ms to draw the image, this is much better than before.
Hope it helps someone.
I'm Currently making a Game, and I need to set a couple Pixels of a BufferedImage (loaded using ImageIO.read) to be transparent in the fastest, best way.
I can't really find any other topic with this question, and If I do the answer Doesn't really help/fit What I need.
Thanks :)
Use Color(red, green, blue, alpha) with values 0-255. Where alpha is the opacity.
Buffed image being of type with an Alpha channel (RGBA, BGRA)
Color halfTransparant = new Color(0x76, 0x54, 0x32, 128);
With setRGB on arrays this still is not fast, you might access the raster data.
But why using dynamically generated images in time critical situations.
The smart way is to create your image with correct alpha from the start (using an image format with transparency, e.g. PNG and your favorite imaging application e.g. GIMP).
Otherwise you can directly alter pixels in the BufferedImage returned by ImageIO using the BufferedImage API: setRGB(int ARGB) and its bulk manipulation cousins in the BufferedImage's Raster.
One way that should work with all image types (as long as they support alpha), is very fast, and will not disable hardware acceleration of the image is:
BufferedImage image = ...; // From somewhere
Graphics2D g = destination.createGraphics();
try {
g.setComposite(AlphaComposite.Clear);
g.fillRect(x, y, w, h); // Area to make transparent
}
finally {
g.dispose();
}
I have a picture (resource) that I would like to use for my application in Android. But I only want to draw specific segments of it. My initial thought is to turn it into a bitmap and specify which pixels need to be drawn and where. I tried canvas.drawBitmap(bitmap, src, dst, null); but it doesn't seem to work. Maybe I am not using it right.
Just wondering if it is possible at all, and what can I use to achieve this?
Thanks!
src = new Rect(20,40,20,40);
dst = new Rect(20,40,20,40);
canvas.drawBitmap(background, offset, 0, null);
canvas.drawBitmap(bitmap, src, dst, null);
I was hoping to see the area specified at src's coordinates to be drawn into the area specified by dst's coordinates, but I don't see anything, other than the background.
dst should be where you want to draw the image in the canvas, and the src should be the Rect you want crop from.
You might want to use a format that supports an alpha channel or load the bitmap as well as a greyscale image for the alpha channel, construct an image from both and draw that. Try Java's Graphics2D object. Here's an article that should get you started.
I have the method below:
private Graphics getBufferedImage(Image image) {
// Create empty BufferedImage, sized to Image
buffImage =
new BufferedImage(
image.getWidth(null),
image.getHeight(null),
BufferedImage.TYPE_INT_ARGB);
// Draw Image into BufferedImage
Graphics wholeImage = buffImage.getGraphics();
return wholeImage;
}
It takes an Image and tries to generate the BufferedImage with a Graphics object.
What can I do with this Graphics (or BufferedImage) to actually let me use it? I'm using GIF files.
Would it be easier to use a byte array to transfer Image data over?
Cheers,
Alex
You can get OutputStream object using method ImageIO.write(...). Now you can transfer it over the network or save to file or store to array or something else.
You can use Graphics.drawImage to draw the original image in the new image. In fact you can use any operation that Graphics offers, as well as cast it to Graphics2D (because that's what it is) and use those operations too.