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();
}
Related
I am creating images in Java that have fonts written on them with a transparent background. I make the fonts different colors and also different types of font styles so I need the program to be dynamic. The issue is that I am using Graphics2D and writing on a Buffered Image using g2d.drawString() and the images aren't nearly the definition I'm looking for. I've tried creating large images with large font sizes and then downscaling but that doesn't work either. I also have set all of the possible RenderingHints to highest definition. I would like the pixel density to be high enough that there isn't much of a difference if you compared it with regular text on a retina screen. Thanks.
To have "retina" quality images in Java, you must create and render your BufferedImage at 2 times the normal size in both dimensions (this will make the image 4 times as large, which I think is what #MadProgrammer means).
Then, you must not downsample (or "scale") the image in Java, but instead keep the BufferedImage in full size, and only draw the image in half size to a native backed Graphics2D instance. The Graphics object passed to the paint() or paintComponent() methods of an AWT or Swing component is normally fine (while the one from BufferedImage.get/createGraphics() isn't).
I've used code like this with success:
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
AffineTransform xform = AffineTransform.getScaleInstance(.5, .5);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(image, xform, null); // image being #2x or "retina" size
}
However, note that the font rendering on modern computer uses "sub pixel antialiasing" or "sub pixel rendering", which is specific to the screen device you are rendering to (see the link, but basically, the RGB pattern or "layout" differs from device to device). This means a BufferedImage usually can't use sub pixel rendering, and thus fonts will look less crisp. If you are rendering to a single LCD screen, you might be able to specify one of the RenderingHints.TEXT_ANTIALIAS_LCD_* rendering hints for better results.
I can use this code
BufferedImage image = ImageIO.read(new File("toolbar.png"));
BufferedImage grayImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
Graphics graphics = grayImage.getGraphics();
graphics.drawImage(image, 0, 0, null);
graphics.dispose();
to convert a BufferedImage from TYPE_3BYTE_BGR to TYPE_3BYTE_BGR.
For example , the input:
the output:
I know that the colored pixel is turned into a white pixel or a black pixel based on a computation rule. What's it and what can I do to adjust it? Thanks for your help in advance.
The conversion as in your code above, is an implicit conversion happening, because the input image (RGB) has more colors that the destination (binary indexed color).
The "rule" for this conversion to binary color can be described as simply "pick the color in the destination that is closest to the one from the input ". As your only colors are black and white, all colors that have the average of R, G and B less than 50% will become black, the rest white. There is no way to "adjust" this, directly.
However, you can control the threshold, either directly or compute it from the input, for better images. See for example this blog for an example.
The BufferedImage class implements Transparency, which has three values:
OPAQUE means no transparency.
TRANSLUCENT means every pixel has an Alpha value between 0 and 1.
BITMASK means every pixel is either opaque or completely transparent.
I can check this value with the getTransparency() method. In my case, I have a PNG file with transparency:
pic = ImageIO.read(new File(filename));
int transparency = pic.getTransparency(); // returns Transparency.TRANSLUCENT
Now I read that images with Transparency.BITMASK can be drawn much faster than those with Transparency.TRANSLUCENT and in my case BITMASK would be enough. I would just color all transparent pixels in one specific color and then save the png without transparency.
Question: How to create a BufferedImage object, which has Transparency.BITMASK from an existing BufferedImage by just defining one color as transparent?
You mean something like...
// Create the buffered image
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
BufferedImage bimage = gc.createCompatibleImage(width, height, Transparency.BITMASK);
Things to note:
If you're PNG contains alpha values > 0 and < 255, they are likely to be rounded to either 0 or 1, possibly making the PNG appear jagged...
If you use Transparency.TRANSLUCENT instead, the color mode of the BufferedImage will be compatible with the GraphicsDevice, making it faster to render
I did an animated sequence a few years ago which was made up of 5 separate images, layered on top of each other and played back at separate speeds all on top of a transparent window...When I first tried running it the, the playback was terrible and jumped about the place.
After some playing around, I found that using Transparency.TRANSLUCENT to convert the images to a compatible color model for the GraphicsDevice worked like a charm...
Nothing wrong with the accepted answer, just providing an alternative for completeness (and I think it will work in headless mode). :-)
The transparency of a BufferedImage is controlled by its ColorModel.
So to create a BufferedImage with a given Transparency constant, you can use code like this:
// Use default RGB color space, no discrete alpha channel,
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorModel colorModel = new ComponentColorModel(cs, true, false, Transparency.BITMASK, DataBuffer.TYPE_BYTE);
WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, w, h, 4, null);
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
I have a BufferedImage which is quite tall (something like 1100 pixels height) and not especially wide (maybe 200-300 pixels). The image consists of a data plot, but most of the pixels represents a background color/value that's not really interesting. My problem is that when the image is resized from a height of 1100 px to something like 200-300 px, many of the pixels that actually contained interesting data is trashed by background color pixels. Is there any way to tell the Java 2D rescaling algorithms that I prefer pixels of certain values (or better, that I want to down-prioritize the background color) while rescaling? I use the following code snippet to rescale the image:
BufferedImage resized = new BufferedImage(width, height, imageType);
Graphics2D graphics2D = resized.createGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); //Has worked best in my case
graphics2D.drawImage(source, 0, 0, width, height, null);
graphics2D.dispose();
I'm sure that there's more effective ways of resizing images in Java, but that's not interesting (at least not for now).
A kind of boring thing is that I used a library before (which will not be used anymore) that actually managed to keep much of the valuable data representing pixels.
Note that I have the raw pixel data at hand (a 2D int array) if that is of any help. The application will handle many images of different sizes, so I don't know for sure that the height necessarily is 1100 pixels.
Thanks.
Have you tried turning on anti-aliasing in the rendering hints? That might help.
If not, you could pretty easily write your own own scaling function. This is actually pretty simple - you just need to:
Loop over all the points in your target BufferedImage
For each of these loop over the relevant points in your raw pixel data (this will be a rectangular zone determined by your scaling factor)
Calculate the target colour using a formula of your choice based on the raw pixel values
Use BufferedImage.setRGB(...) to set the colour of the target pixel
Normal anti-aliasing would use something like an average of the source pixel values as a formula, it sounds like you want something that gives priority to particular values (e.g. take the maximum pixel value if your background pixels have low colour values)
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.