How to render java component to high resolution image? - java

I'm trying to extract an images of the application window's components in java.
I've created a class that overrides java.awt.GraphicsConfiguration and looks very similar to sun.awt.Win32GraphicsConfig. The problem is that when I call the createAcceleratedImage function with component (for example application window) and 2x larger dimensions than the real one, the java renders it small in the image on the top left corner and the rest of the image is empty space.
My createAcceleratedImage function is same as in sun.awt.Win32GraphicsConfig:
public Image createAcceleratedImage(Component target, int w, int h) {
ColorModel localColorModel = getColorModel(Transparency.OPAQUE);
WritableRaster localWritableRaster = localColorModel.createCompatibleWritableRaster(w, h);
return new OffScreenImage(target, localColorModel, localWritableRaster, localColorModel.isAlphaPremultiplied());
}
I apologize if I forgot to mention more information, If you need more information please ask in comments.
Thank you for help.

I used VolatileImage instead of OffScreenImage inside my CustomComponentPeer.
public VolatileImage createVolatileImage(int width, int height) {
GraphicsConfiguration graphicsConfig = getGraphicsConfiguration();
return graphicsConfig.createCompatibleVolatileImage(width, height);
}
I found that drawImage function in Graphics2D support high resolution rendering only for MultiResolutionImage and VolatileImage. Rendering in high DPI using OffScreenImage is not supported.

Related

How to take screenshot of any java native application without bringing it to foreground in Java?

I want to take the screenshot of a java native application ( any framework AWT, Swing, JavaFx ) without bringing it to the foreground. Are there any framework-specific methods available for this?
I have tried using Robot class to get the screenshot
private static void capture(int x, int y , int width , int height, String setName) {
Robot robot = new Robot();
Rectangle area = new Rectangle(x, y, width, height);
BufferedImage image = robot.createScreenCapture(area);
ImageIO.write(image, "png", new File(System.getProperty("user.dir") + "\\images\\" + setName +".png"));
}
Now robot class with just take the area coordinates and capture the image, whether the target application is on the top or not, to get the application on the top I am using JNA to bring it to focus
private static void bringToFocus() {
for (DesktopWindow desktopWindow : WindowUtils.getAllWindows(true)) {
if (desktopWindow.getTitle().contains("notepad")) {
HWND hwnd = User32.INSTANCE.FindWindow(null, desktopWindow.getTitle());
User32.INSTANCE.SetForegroundWindow(hwnd);
break;
}
}
}
But this is an example where we need to capture only one application, if we need to capture 10 applications screenshots we need to one by one bring them to the front and capture and bring next.
Is there any framework specific method availabe which can take the application screenshot without bringing it to the front.
If your screen shot only needs to be of the Java GUI, you can paint to a BufferedImage
public static Image screenShot(Component c) {
BufferedImage im = new BufferedImage(c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics g = im.getGraphics();
c.paint(g); // Paint is the proper entry point to painting a (J)Component, rather than paintComponent
g.dispose(); // You should dispose of your graphics object after you've finished
return im;
}
If your requirement is to paint the Java GUI component along with the rest of the screen, but like your java (J)Frame is in front, you can do that by painting the screen using the robot first, then doing what I've posted above, but with the BufferedImage (which has already been drawn on) being passed in as a parameter rather than being created in the method.

Why drawing pixels directly is slower than drawing data in a BufferedImage?

Currently I'm trying to create something to display a "2D view" for a Grid which can be used for various proporses, such as a simple 2D game.
After doing a wrap work with a simple Core class (this holds all information to my visual stuff, including pixels values for sprites) I started the visual work. As my aim is to do this with plain swing/awt in Java I just created a custom JComponent to this.
In my JComponent I tried some approaches and decided to test draw my pixels info in a BufferedImage and at other hand draw the pixels directly in Graphics object.
Looking through internet I found some ways to draw a pixel/dot on screen, like this. In this case, I wrapped this in a method:
private void drawSpritePixels(ArrayList<Spr.Pixel> pixels, int startX, int startY, Graphics2D g) {
for (Spr.Pixel p : pixels){
g.setColor(p.color);
g.fillRect(p.x + startX, p.y + startY, 1, 1);
}
}
For drawing my data onto a BufferedImage, I'm using this:
public BufferedImage spriteImage(int spriteAddress) {
BufferedImage img = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration()
.createCompatibleImage(32, 32, Transparency.TRANSLUCENT);
for (Pixel p : getSpriteInfo(spriteAddresses.get(spriteAddress))) {
img.setRGB(p.x, p.y, p.color.getRGB());
}
return img;
}
As title says, the performance was better using BufferedImage approach. Im my tests BufferedImage takes 40ms to finish while Pixels takes 200ms+ to the same amount of "work".
Then, why this is so different? Should do I avoid any iterations in pixels approach?

Why does VolatileImage have no set/getPixel() method

I am a relative newbie in game programming. I know how to draw pixels to a BufferedImage using setPixel(). It is horribly slow on larger formats so I moved on and found VolatileImage (took me a week or so). It is fairly easy to draw lines, strings, rects, etc but I can't draw individual pixels. I already tried using drawLine(x,y,x,y) but I get 3-4 FPS on an 800x600 image.
The fact that java didn't include setPixel() or setRGB() in the VolatileImage makes me pretty angry and confused.
I have 4 questions:
Is there a way to draw individual pixels on a VolatileImage? (on 1440x900 formats with FPS > 40)
Can I draw pixels in a BufferedImage with a faster method? (same 1440x900, FPS > 40)
Is there any other way to draw pixels fast enough for 3D games?
Can I make my BufferedImage hardware accelerated( tried using setAccelerationPriority(1F) but it doesn't work)
Please if you have any idea tell me. I can't continue making my game wihout this information. I already made 3D rendering algorithms but i need to be able to draw fast pixels. I have got a good feeling about this game.
Here's the code if it can help you help me:
public static void drawImageRendered (int x, int y, int w, int h) { // This is just a method to test the performance
int a[] = new int[3]; // The array containing R, G and B value for each pixel
bImg = Launcher.contObj.getGraphicsConfiguration().createCompatibleImage(800, 600); // Creates a compatible image for the JPanel object i am working with (800x600)
bImg.setAccelerationPriority(1F); // I am trying to get this image accelerated
WritableRaster wr = bImg.getRaster(); // The image's writable raster
for (int i = 0; i < bImg.getWidth(); i++) {
for (int j = 0; j < bImg.getHeight(); j++) {
a[0] = i % 256;
a[2] = j % 256;
a[1] = (j * i) % 256;
wr.setPixel(i, j, a); // Sets the pixels (You get a nice pattern)
}
}
g.drawImage(bImg, x, y, w, h, null);
}
I would much prefer not using OpenGL or any other external libraries, just plain Java.
Well you're basically drawing one pixel after the other using the CPU. There's no way that this can be accelerated, thus such a method does simply not make any sense for a VolatileImage. The low FPS you get suggest that this even causes a significant overhead, as each pixel drawing operation is sent to the graphics card (with information such as location & colour), which takes longer than to modify 3 or 4 bytes of RAM.
I suggest to either stop drawing each pixel separately or to figure out a way to make your drawing algorithm run directly on the graphics card (which most likely requires another language than Java).
It's been over 4 years since this post got an answer. I was looking for an answer to this question as well and stumbled on this post. After some more searching, I got it to work. Below I'll post the source to rendering pixels with a VolatileImage.
It seems Java hides our ability to plot pixels directly to a VolatileImage, but we can draw buffered images to it. For good reason. Using the software to plot a pixel doesn't really help with acceleration(in Java it seems). If you can plot pixels to a BufferedImage, and then render it on a VolatileImage, you may get a speed bonus since it's hardware accelerated from that point.
The source down below is a self-contained example. You can copy-pasta practically all of it to your project and run it.
https://github.com/Miekpeeps/JavaSnippets-repo/blob/master/src/graphics_rendering/pixels_03/PlottingVolatile.java
In the constructor I save the Graphics environment of the app/game.
private GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
private GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
Then, when I call a method to enable hardware we create a buffer. I set the transparency to Opaque. In my little engine, I deal with transparency/alpha blending on another thread in the pipeline.
public void setHardwareAcceleration(Boolean hw)
{
useHW = hw;
if (hw)
{
vbuffer = gc.createCompatibleVolatileImage(width, height, Transparency.OPAQUE);
System.setProperty("sun.java2d.opengl", hw.toString()); // may not be needed.
}
}
For each frame I update, I get the Graphics from the VolatileImage and render my buffer there. Nothing gets rendered if I dont flush().
#Override
public void paintComponent(Graphics g)
{
if(useHW)
{
g = vbuffer.getGraphics();
g.drawImage(buffer, 0, 0, null);
vbuffer.flush();
}
else
{
g.drawImage(buffer, 0, 0, null);
buffer.flush();
}
}
There is still a little bit of overhead when calling to plot a pixel on the BufferedImage writable raster. But when we update the screen, we get a speed boost when using the Volatile image instead of using the Buffered image.
Hope this helps some folks out. Cheers.

Swing and bitmaps on retina displays

I've got a Java desktop app that works, amongst other, on OS X.
Now the new MacBook Pro has a retina display and I'm concerned: how is it going to work regarding Swing?
What about when a Java app uses both Swing components and some bitmap graphics (like custom icons / ImageIcon)?
Shall all desktop Java apps be automatically resized (for example by quadrupling every pixel) or am I going to need to create two versions of my icons set (for example one with 24x24 icons and the other with 96x96 icons) and somehow determine that the app is running on a retina display?
Use IconLoader library. It supports HiDPI images http://bulenkov.com/iconloader/ It also provides a way to work with HiDPI images (drawing, etc)
On Apple's Java 6 you can provide multiple versions of the same image. Depending on the screen (retina or not), one or the other image is picked and drawn.
However, those images have to loaded in a special way:
Toolkit.getDefaultToolkit().getImage("NSImage://your_image_name_without_extension");
For example, if your (regular resolution) image is called: "scissor.png", you have to create a high resolution version "scissor#2x.png" (following the Apple naming conventions) and place both images in the Resources directory of your app bundle (yes, you need to bundle your app).
Then call:
Image img = Toolkit.getDefaultToolkit().getImage("NSImage://scissor");
You can use the resulting image in your buttons and it will be drawn with the right resolution magically.
There are two other "tricks" you can use:
Using an AffineTransform of (0.5, 0.5) on your Graphics2D object before drawing an Image. Also see this java-dev message
Creating a high dpi version of your image programmatically using this hack
The first "trick" (0.5 scaling) by now also works on Oracle's Java 7/8.
I.e. if you draw an image with 0.5 scaling directly to the component's Graphics object, it will be rendered in high resolution on Retina displays (and also with half its original size).
Update
Starting with Java 9, there is better built-in support for images with different resolutions via the MultiResolutionImage interface. For more details, please see this answer.
I can confirm that the scaling your images works with on Oracle Java 1.8. I cannot get the NSImage hack to work on java 1.7 or 1.8. I think this only works with Java 6 from Mac...
Unless someone else has a better solution, what I do is the following:
Create two sets of icons.
If you have a 48pixel width icon create one 48px #normal DPI and another at 96px with 2x DPI. Rename the 2xDPI image as #2x.png to conform with apple naming standards.
Subclass ImageIcon and call it RetinaIcon or whatever.
You can test for a Retina display as follows:
public static boolean isRetina() {
boolean isRetina = false;
GraphicsDevice graphicsDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
try {
Field field = graphicsDevice.getClass().getDeclaredField("scale");
if (field != null) {
field.setAccessible(true);
Object scale = field.get(graphicsDevice);
if(scale instanceof Integer && ((Integer) scale).intValue() == 2) {
isRetina = true;
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return isRetina;
}
Make sure to #Override the width and height of the new ImageIcon class as follows:
#Override
public int getIconWidth()
{
if(isRetina())
{
return super.getIconWidth()/2;
}
return super.getIconWidth();
}
#Override
public int getIconHeight()
{
if(isRetina())
{
return super.getIconHeight()/2;
}
return super.getIconHeight();
}
Once you have a test for the retina screen and your custom width/height methods overridden you can customise the painIcon method as follows:
#Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y)
{
ImageObserver observer = getImageObserver();
if (observer == null)
{
observer = c;
}
Image image = getImage();
int width = image.getWidth(observer);
int height = image.getHeight(observer);
final Graphics2D g2d = (Graphics2D)g.create(x, y, width, height);
if(isRetina())
{
g2d.scale(0.5, 0.5);
}
else
{
}
g2d.drawImage(image, 0, 0, observer);
g2d.scale(1, 1);
g2d.dispose();
}
I do not know how this will work with multiple screens though- is there anyone else that can help out with that???
Hope this code helps out anyway!
Jason Barraclough.
Here is an example of using the scaling as mentioned above:
RetinaIcon is on the left. ImageIcon is on the right
Here is a solution, that works also when the icons are used in the apple menu. There the icon is automatically greyed. So I have implemented a class DenseIcon which paints densely:
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
if(getImageObserver() == null) {
g.drawImage(getImage0(), x, y, getIconWidth(), getIconHeight(), c);
} else {
g.drawImage(getImage0(), x, y, getIconWidth(), getIconHeight(), getImageObserver());
}
}
How to hook into the greying I have not yet figured out. So as a kludge we return a low res image so that the menu can do its modifications:
public Image getImage() {
Image image = getImage0().getScaledInstance(
getIconWidth(),
getIconHeight(),
Image.SCALE_SMOOTH);
ImageIcon icon = new ImageIcon(image, getDescription());
return icon.getImage();
}
You find the code of the full class here on gist. You need to instantiate the icon class with an URL to an image that is twice the size. Works for 2K displays.
This how icons look like on my retina macbook '12:
On the left side icons in IntelliJ IDEA 11 (swing app) and on the right side IDEA 12 which is claimed to be retinized. As you can see automatically resized icons (on the left) looks pretty ugly.
As far as I know, they, just like the guys from Chrome team, made it by providing double sized icons.

How can I edit a jpg image through Java?

I have loaded a jpg image in which I want to draw letters and circles, given a x,y coordinate.
I have been trying to figure out the paintIcon of the ImageIcon class
public void paintIcon(Component c,
Graphics g,
int x,
int y)
Does this method allow me to edit jpg images the way I want to? What are supposd to be the Component c and Graphics g paramethers? What would I add to its body to paint circles or letters?
I'm working on Netbeans 6.5, do I have anything builtin for this task (instead of ImageIcon)?
The pure-Java way is to use ImageIO to load the image as a BufferedImage. Then you can call createGraphics() to get a Graphics2D object; you can then draw whatever you want onto the image.
You can use an ImageIcon embedded in a JLabel to do the displaying, and you can add a MouseListener and/or a MouseMotionListener to the JLabel if you're trying to allow the user to edit the image.
Manipulating images in Java can be achieved by using the Graphics or Graphics2D contexts.
Loading images such as JPEG and PNG can be performed by using the ImageIO class. The ImageIO.read method takes in a File to read in and returns a BufferedImage, which can be used to manipulate the image via its Graphics2D (or the Graphics, its superclass) context.
The Graphics2D context can be used to perform many image drawing and manipulation tasks. For information and examples, the Trail: 2D Graphics of The Java Tutorials would be a very good start.
Following is a simplified example (untested) which will open a JPEG file, and draw some circles and lines (exceptions are ignored):
// Open a JPEG file, load into a BufferedImage.
BufferedImage img = ImageIO.read(new File("image.jpg"));
// Obtain the Graphics2D context associated with the BufferedImage.
Graphics2D g = img.createGraphics();
// Draw on the BufferedImage via the graphics context.
int x = 10;
int y = 10;
int width = 10;
int height = 10;
g.drawOval(x, y, width, height);
g.drawLine(0, 0, 50, 50);
// Clean up -- dispose the graphics context that was created.
g.dispose();
The above code will open an JPEG image, and draw an oval and a line. Once these operations are performed to manipulate the image, the BufferedImage can be handled like any other Image, as it is a subclass of Image.
For example, by creating an ImageIcon using the BufferedImage, one can embed the image into a JButton or JLabel:
JLabel l = new JLabel("Label with image", new ImageIcon(img));
JButton b = new JButton("Button with image", new ImageIcon(img));
The JLabel and JButton both have constructors which take in an ImageIcon, so that can be an easy way to add an image to a Swing component.
Use a library to do just that. One that you might try is JMagick.
I have used Java Advanced Imaging library (http://java.sun.com/products/java-media/jai/forDevelopers/jaifaq.html), but you can also look at ImageJ (http://rsbweb.nih.gov/ij/index.html)
I imagen you could use this method to overlay the elements you need every time the image is drawn in the UI (this would happen numerous times as you are not drawing ON the image data its self) but may be suitable to your purposes (and advantageous if the overlay changes over time).
Something like:
new ImageIcon("someUrl.png"){
public void paintIcon(Component c, Graphics g, int x, int y) {
super(c, g, x, y);
g.translate(x, y);
g.drawOval(0, 0, 10, 10);
...
g.translate(-x, -y);
}
};
Having said that, mmyers' answer is much better if you want to modify the image data.

Categories